C : Pointer(포인터)

m_ngyeong·2024년 8월 8일
0

C

목록 보기
7/16
post-thumbnail

포인터 개념에 들어가기 앞서, 데이터의 주소값이란 해당 데이터가 저장된 메모리의 시작 주소를 의미한다. C언어에서는 이러한 주소값을 1바이트 크기의 메모리 공간으로 나누어 표현한다.

Pointer(포인터)

포인터(pointer)는 메모리 주소를 저장하는 변수 즉, 메모리의 주소값을 저장하는 변수이며, 포인터 변수라고도 부른다.

포인터를 이해하면 동적 메모리 할당, 배열, 함수, 문자열 등을 효과적으로 다룰 수 있다.


1. 포인터 선언과 초기화

포인터 변수는 선언할 때 자료형 뒤에 *를 붙여 선언한다.

int n = 100;   // 정수형 변수의 선언
int* ptr;      // 정수형 포인터 변수 선언
int a = 10;
ptr = &a;      // a의 주소를 ptr에 저장
  • int* ptr;: 정수형 데이터를 가리키는 포인터 변수 ptr을 선언.
  • ptr = &a;: 변수 a의 주소를 ptr에 저장하며, & 연산자는 변수의 주소를 얻음.

💚 포인터 연산자 :

- * (참조 연산자) :

참조 연산자는 포인터의 이름이나 주소 앞에 사용하여, 포인터에 가리키는 주소에 저장된 값을 반환한다.

* 는 포인터를 선언하거나 메모리에 접근할 때도 사용되지만 이항 연산자로 사용하면 곱셈 연산으로 쓸 수 있다.

- & (주소 연산자) :

주소 연산자는 변수의 이름 앞에 사용하여, 해당 변수의 주소값을 반환한다. &기호는 앰퍼샌드(ampersand)라고 읽으며, 번지 연산자라고도 불린다.

💚 포인터 선언

[DataType] *[Pointer Name];
  • DataType : 포인터가 가리키고자하는 변수의 타입을 명시
  • Pointer Name : 포인터가 선언된 이후에 포인터에 접근하기 위해 사용

포인터를 선언한 후 참조 연산자(*)를 사용하기 전에 포인터는 반드시 먼저 초기화되어야 한다. 그렇지 않으면 의도하지 않은 메모리의 값을 변경하게 되기 때문에 오류가 발생한다.

[DataType] *[Pointer Name] = &[Variable Name];
[DataType] *[Pointer Name] = Address Value;

2. 포인터를 통한 값 접근

포인터를 통해 해당 주소에 저장된 값에 접근할 수 있다.

#include <stdio.h>

int main() {
    int a = 10;
    int* ptr = &a;

    printf("Value of a: %d\n", a);        // a의 값 출력
    printf("Address of a: %p\n", &a);     // a의 주소 출력
    printf("Value at ptr: %d\n", *ptr);   // 포인터 ptr이 가리키는 값 출력

    *ptr = 20;                            // 포인터를 통해 값 변경
    printf("New value of a: %d\n", a);    // 변경된 a의 값 출력

    return 0;
}
  • *ptr: 포인터 ptr이 가리키는 주소의 값을 의미.
// Output: 
Value of a: 10
Address of a: 0x7fff3541f4b4
Value at ptr: 10
New value of a: 20

3. NULL 포인터

NULL 포인터는 어떤 유효한 메모리 주소도 가리키지 않는 포인터이다. 초기화되지 않은 포인터는 사용하기 전에 NULL로 설정하는 것이 좋다.

int* ptr = NULL;
if (ptr == NULL) {
    printf("ptr is NULL\n");
}

4. 포인터와 배열

배열의 이름은 배열의 첫 번째 요소를 가리키는 포인터로 간주된다.

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int* ptr = arr;  // arr은 arr[0]의 주소를 가리킴

    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, *(ptr + i));  // 포인터 연산을 통해 배열 요소에 접근
    }

    return 0;
}
  • ptr + i: 포인터 연산을 통해 다음 요소의 주소로 이동.
  • *(ptr + i): 해당 주소의 값을 가져옴.
// Output: 
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
  • Another example :
#include <stdio.h>

int main() {
    int arr[] = {1,2,3,4,5};
    int *p = arr + 1;
    printf("arr[0] = %d, p[0] = %d", arr[0], p[0]);
    
    return 0;
}
// Output: 
arr[0] = 1, p[0] = 2

포인터와 이차원 배열

#include <stdio.h>
int main() {
    int matrix[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
    printf("%d\n", *matrix); // matrix[0]의 주소값 출력
    printf("%d\n", **matrix); // matrix[0][0]의 값 출력

    int *p = *matrix;
    printf("%d\n", *(p+1));
    
    int *p_1 = matrix[1];
    printf("%d", *(p_1+1));
    
    return 0;
}
  • *matrix : matrix가 가리키는 주소의 값을 의미.
  • **matrix
  • *(p+1) : 포인터 연산을 통해 다음 요소의 주소로 이동 후 해동 주소의 값을 가져옴.
  • *(p_1+1) : 포인터 연산을 통해 다음 요소의 주소로 이동 후 해동 주소의 값을 가져옴.
-1111717584
1
2
5

5. 포인터와 함수

포인터는 함수 인자로 사용할 수 있으며, 함수가 호출될 때 원본 데이터에 직접 접근하거나 수정할 수 있게 한다.

#include <stdio.h>

void increment(int* num) {
    (*num)++;  // 포인터를 통해 원본 변수 값 증가
}

int main() {
    int a = 10;
    increment(&a);
    printf("Incremented value of a: %d\n", a);

    return 0;
}
  • void increment(int* num): 정수형 포인터를 인자로 받는 함수.
  • (*num)++: 포인터를 통해 원본 변수 값을 증가시킴.

6. 동적 메모리 할당

동적 메모리 할당은 mallocfree 함수를 사용하여 수행한다.

malloc()

malloc() 함수는 동적 메모리를 할당하는 함수로, 표준 라이브러리 헤더 <stdlib.h>에 정의되어 있다. 이 함수는 인자로 받은 크기만큼의 바이트 수에 해당하는 메모리를 힙(heap) 영역에서 할당하고, 할당된 메모리의 시작 주소를 반환한다.

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* ptr = (int*)malloc(sizeof(int));  // 정수형 크기만큼 메모리 할당

    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    *ptr = 100;  // 동적 할당된 메모리에 값 저장
    printf("Value at ptr: %d\n", *ptr);

    free(ptr);  // 메모리 해제
    return 0;
}
  • malloc(sizeof(int)): 정수형 크기만큼 메모리를 동적으로 할당.
  • free(ptr): 동적으로 할당된 메모리를 해제.


참고,
https://www.tcpschool.com/c/c_pointer_intro

profile
사용자 경험 향상과 지속적인 성장을 추구하는 프론트엔드 개발자 ʚȉɞ

0개의 댓글