C : Double Pointer(이중 포인터)

m_ngyeong·2024년 10월 15일
0

C

목록 보기
11/16
post-thumbnail

Double Pointer(이중 포인터)

이중 포인터(double pointer)포인터를 가리키는 포인터로 포인터에 대한 포인터를 의미한다. 이중 포인터는 단일 포인터와 달리, 포인터 변수가 저장된 메모리 주소를 가리키는 포인터이다.

이중 포인터는 C와 C++에서 다양한 목적으로 사용되며, 특히 동적 메모리 할당, 포인터 배열, 함수에서 포인터 값을 변경할 때 자주 사용된다.


이중 포인터 선언

이중 포인터는 선언할 때 자료형 뒤에 **를 붙여 선언하다.

int **ptr;
  • ptr은 이중 포인터로, int형 변수를 가리키는 포인터를 가리키는 포인터.

이중 포인터의 기본 개념

이중 포인터는 다음과 같은 형태로 메모리에 존재한다:

  • int형 변수가 있고, 그 변수를 가리키는 포인터가 있음.
  • 포인터의 주소를 저장하는 또 다른 포인터가 존재하는 것이 이중 포인터.

간단한 예시:

int x = 10;       // 정수 변수
int *p = &x;      // x를 가리키는 포인터
int **pp = &p;    // p를 가리키는 이중 포인터
  • x는 정수 값 10을 가짐.
  • px의 주소를 가리키며, *px의 값을 참조.
  • ppp의 주소를 가리키며, *ppp를 참조하고, **ppx의 값을 참조.

이중 포인터 사용 예시:

#include <stdio.h>

int main() {
    int x = 10;        // 정수 변수 x
    int *p = &x;       // x를 가리키는 포인터 p
    int **pp = &p;     // p를 가리키는 이중 포인터 pp

    // 값 출력
    printf("x: %d\n", x);        // x의 값 출력
    printf("*p: %d\n", *p);      // p가 가리키는 값, 즉 x의 값 출력
    printf("**pp: %d\n", **pp);  // pp가 가리키는 포인터가 가리키는 값, 즉 x의 값 출력
    
    // 주소 출력
    printf("\n주소 출력:\n");
    printf("&x: %p\n", (void*)&x);     // x의 주소
    printf("p: %p\n", (void*)p);       // p가 가리키는 주소 (x의 주소)
    printf("&p: %p\n", (void*)&p);     // p의 주소
    printf("pp: %p\n", (void*)pp);     // pp가 가리키는 주소 (p의 주소)
    printf("&pp: %p\n", (void*)&pp);   // pp의 주소

    return 0;
}

출력

x: 10
*p: 10
**pp: 10

주소 출력:
&x: 0x7ffee4bff75c
p: 0x7ffee4bff75c
&p: 0x7ffee4bff758
pp: 0x7ffee4bff758
&pp: 0x7ffee4bff754
  • &x: 변수 x의 메모리 주소를 출력.
  • p: 포인터 p가 가리키는 주소, 즉 x의 주소를 출력.
  • &p: 포인터 p 자체의 주소를 출력.
  • pp: 이중 포인터 pp가 가리키는 주소, 즉 p의 주소를 출력.
  • &pp: 이중 포인터 pp 자체의 주소를 출력.

printf에서 %p 형식 지정자

  • %p는 포인터 주소를 출력할 때 사용하는 형식 지정자.
  • 포인터 주소를 출력할 때는 일반적으로 (void*)로 캐스팅.

이중 포인터의 주요 용도

이중 포인터는 포인터를 가리키는 포인터이다.
이중 포인터는 포인터의 주소를 저장하며, 이를 통해 포인터의 값이나 메모리를 간접적으로 제어할 수 있다. 동적 메모리 할당, 포인터 배열 처리, 함수에서 포인터 값 변경 등 여러 상황에서 유용하게 사용된다.

동적 메모리 할당

이중 포인터는 다차원 배열의 동적 메모리 할당에서 자주 사용된다. 예를 들어, 2차원 배열을 동적으로 할당할 때 이중 포인터를 사용한다.

  • malloc(): 동적 메모리를 할당하는 함수로, 인자로 받은 크기만큼의 바이트 수에 해당하는 메모리를 힙(heap) 영역에서 할당하고 할당된 메모리의 시작 주소를 반환함.
  • free(): 동적으로 할당된 메모리를 해제.
#include <stdio.h>
#include <stdlib.h>

int main() {
    int rows = 3, cols = 4;
    int **arr;

    // 2차원 배열을 위한 메모리 할당
    arr = (int **)malloc(rows * sizeof(int *));  // 각 행에 대한 포인터 배열 할당
    for (int i = 0; i < rows; i++) {
        arr[i] = (int *)malloc(cols * sizeof(int));  // 각 행의 열에 대한 메모리 할당
    }

    // 배열 초기화 및 출력
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            arr[i][j] = i * cols + j;
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }

    // 할당된 메모리 해제
    for (int i = 0; i < rows; i++) {
        free(arr[i]);  // 각 행의 메모리 해제
    }
    free(arr);  // 포인터 배열 해제

    return 0;
}


// 출력 결과:
0 1 2 3 
4 5 6 7 
8 9 10 11 
  • arr: 이중 포인터로서, 동적으로 2차원 배열을 할당하는 데 사용됨.
  • (int **)malloc(rows * sizeof(int *)):
    • rows 개의 포인터 배열을 위해 메모리를 할당. 각 포인터(배열의 한 행)는 int * 타입을 가짐.
    • malloc()이 반환한 메모리 주소를 정수 포인터를 가리키는 포인터(int **)로 캐스팅.
  • (int *)malloc(cols * sizeof(int)):
    각 행에 대해 cols개의 정수 메모리를 할당하여 실제 2차원 배열을 형성.

◾️ (int **) :

캐스팅(casting)으로, malloc() 함수가 반환하는 메모리 주소를 이중 포인터(int **)로 변환하는 것이다.

  • int **는 이중 포인터로, 정수형 포인터를 가리키는 포인터를 의미.
  • malloc은 기본적으로 void *를 반환하므로, 이를 정수형 포인터를 가리키는 포인터로 캐스팅해줘야 안전하게 사용할 수 있음.

◾️ malloc(rows * sizeof(int *)) :

rows개의 포인터를 가리키는 메모리 공간을 할당한다. rows만큼의 포인터 배열을 위한 메모리 공간 확보하는 것이다. 이 배열의 각 요소는 정수 배열의 행을 가리키는 포인터가 된다.

  • rows : 할당할 포인터 배열의 크기(배열의 행 수).
  • sizeof(int *) : int *의 크기(바이트 수)를 의미하며, 각 배열 요소가 정수형 포인터(int *)이므로, sizeof(int *)를 사용해 포인터 크기만큼의 메모를 할당함.

포인터 배열에 대한 접근

이중 포인터는 포인터 배열에 접근할 때 사용될 수 있다. 예를 들어, 문자열 배열을 처리할 때 이중 포인터를 사용할 수 있다.

#include <stdio.h>

int main() {
    char *arr[] = {"Hello", "World", "C programming"};
    char **ptr = arr;

    for (int i = 0; i < 3; i++) {
        printf("%s\n", *(ptr + i));
    }

    return 0;
}


// 출력 결과:
Hello
World
C programming
  • arr : 문자열을 가리키는 포인터들의 배열.
  • ptr : 그 배열을 가리키는 이중 포인터.

함수에서 포인터 변경

함수에서 포인터의 값을 변경하려면 이중 포인터가 필요하다. 예를 들어, 동적으로 할당된 메모리를 함수에서 초기화하고, 이를 호출하는 함수에서 참조하고 싶을 때 이중 포인터를 사용할 수 있다.

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

void allocateMemory(int **p) {
    *p = (int *)malloc(sizeof(int));  // 동적 메모리를 할당
    **p = 100;  // 할당된 메모리에 값 100 저장
}

int main() {
    int *p = NULL;
    allocateMemory(&p);  // p의 주소를 넘김

    printf("p가 가리키는 값: %d\n", *p);

    free(p);  // 동적 메모리 해제
    return 0;
}


// 출력 결과:
p가 가리키는 값: 100
  • allocateMemory() 함수는 이중 포인터 int **p를 받아서 동적 메모리를 할당하고, 그 메모리에 값을 저장.
    이중 포인터가 사용되는 이유는 함수 내부에서 포인터의 값을 변경하기 위해서임.


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

0개의 댓글