C 포인터 사용 - sonkoni/Koni-Wiki GitHub Wiki

자료형 *포인터     : 포인터변수 선언. 여기서 자료형이란 포인터가 가리키는 주소에 실제로 있는 value 의 타입임
*포인터          : 역참조(포인터가 가리키는 주소의 내부 공간에 있는 값을 가져옴)
포인터 = &변수    : 변수의 메모리 주소를 포인터에 저장(`&변수` 를 통해 주소 추출)
*포인터 = 값      : 포인터를 역참조하여 값을 저장(포인터가 가리키는 주소의 내부 공간에 값을 심음)

   numPtr        num                      *numPtr       num
  ┌─────────┐   ┌─────────┐               ┌─────────┐  ┌─────────┐
  │  Addr ──┼──▶│  value  │               │  Addr ──┼──┼─▶ value │
  └─────────┘   └─────────┘               └─────────┘  └─────────┘
  numPtr을 호출하면 자기 자신의 값을 반환한다.     역참조 *numPtr은 numPtr에 저장된 주소값을 파고 들어간다는 의미이다.
  numPtr은 &num을 저장하고 있으므로             여기서 numPtr 은 &num을 저장하고 있으므로
  `numPtr`을 호출하면 num의 주소값를 반환한다.    여기서는 num의 주소를 파고 들어간 값 value를 반환한다.

int *numPtr;            // 포인터 선언. `*numPtr` 로 역참조 호출했을 때 int 형 value가 있다는 의미이다.
printf("%p", numPtr);   // 포인터변수. 내부에 저장된 `가리키는 주소`를 반환
printf("%d", *numPtr);  // 포인터 역참조. 포인터변수 내에 저장된 `가리키는 주소`를 파고 들어간다는 의미. 여기서는 실제 값 반환.
#include <stdio.h>

int main(int argc, char *argv[]) {
    int num = 10;
    printf("=> %p\n", &num);   // num 의 주소 추출
    // => 0x7ff7b64885bc
    
    int *ptr = &num;         // 포인터 ptr 을 선언하고 num 의 주소 담기
    printf("=> %d\n", *ptr); // 포인터역참조. ptr이 가리키는 주소의 실제값
    // => 10
    
    int *numPtr;        // 포인터 numPtr을 선언하기
    numPtr = &num;      // 포인터 numPtr에 num 주소 할당
    *numPtr = 20;       // 포인터 numPtr의 역참조를 통해 실제 값을 20으로 재할당
    printf("=> %d, %d\n", *numPtr, num); // 둘다 10에서 20으로 바뀜.
    // => 20, 20

    printf("numPtr Size : %zu\n", sizeof(numPtr));  
    printf("*numPtr Size: %zu\n", sizeof(*numPtr));
    printf("int num Size: %zu\n", sizeof(num));
    // numPtr Size : 8
    // *numPtr Size: 4
    // int num Size: 4
    
    return 0;
}

이중포인터 사용

자료형 **포인터이름;

             &numPtr2      &numPtr1       &num
             ┌─────────┐   ┌─────────┐   ┌─────────┐
             │&numPtr1─┼──▶│  &num ──┼──▶│    10   │
             └─────────┘   └─────────┘   └─────────┘

             ╔═════════╗   ┌─────────┐   ┌─────────┐
    numPtr2: ╫▶&numPtr1║   │         │   │         │
             ╚═════════╝   └─────────┘   └─────────┘
             ┌─────────┐   ╔═════════╗   ┌─────────┐
   *numPtr2: ┼─────────┼───╫─▶ &num  ║   │         │
             └─────────┘   ╚═════════╝   └─────────┘
             ┌─────────┐   ┌─────────┐   ╔═════════╗
  **numPtr2: ┼─────────┼───┼─────────┼───╫──▶ 10   ║
             └─────────┘   └─────────┘   ╚═════════╝

포인터변수 자체의 주소는 일반 포인터에 저장할 수 없고 이중 포인터에 저장해야 한다. 포인터의 주소를 저장할 수 있을 때 이중 이상의 포인터가 된다. 이중포인터는 매우 빈번하게 쓰이는데, 삼중부터는 거의 보지 못했다.

포인터 선언을 다시 한 번 의미있게 봐야 한다. 선언 int **numPtr2;는 역참조 **numPtr2 로 했을 때 int 형 value 가 있다는 의미이다. 따라서 numPtr2를 호출하거나 *numPtr2를 호출했을 때에는 주소값만 존재한다.

#include <stdio.h>

int main(int argc, char *argv[]) {
    int num = 10;   // 변수 : value 를 담을 수 있는 storage.
    int *numPtr1;   // 단일 포인터: storage 의 Address 를 담을 수 있다.
    int **numPtr2;  // 이중 포인터: pointer 의 Address 를 담을 수 있다.

    numPtr1 = &num;
    numPtr2 = &numPtr1;  // numPtr1의 주소를 저장
    
    printf("=> %p[%d]\n", &num, num);
    printf("=> %p[%p] => %d\n", &numPtr1, numPtr1, *numPtr1);
    printf("=> %p[%p] => %p => %d\n", &numPtr2, numPtr2, *numPtr2, **numPtr2);
    // => 0x7ff7b0c075ac[10]
    // => 0x7ff7b0c075b8[0x7ff7b0c075ac] => 10
    // => 0x7ff7b0c075b0[0x7ff7b0c075b8] => 0x7ff7b0c075ac => 10
    //
    return 0;
}
         &numPtr2
         0x7ff7b0c075b0
         ┌─────────────────┐      &numPtr1
  ┌  ┌   │Addr(&numPtr1) ──┼────▶ 0x7ff7b0c075b8
  │ *│   └─────────────────┘     ┌─────────────────┐      &num
**│  ╞                           │    Addr(&num) ──┼────▶ 0x7ff7b0c075ac
  │ *│                           └─────────────────┘     ┌─────────────┐
  └  └                                                   │     10      │
                                                         └─────────────┘

void 포인터

void *포인터이름;

자료형이 정해지지 않은 포인터이다. 어떤 자료형이든 지정할 수 있으며 컴파일러 경고도 없다. 이러한 특성으로 인해 역참조할 수 없다.

#include <stdio.h>

int main(int argc, char *argv[]) {
    int num = 10;
    char ch = 'a';
    int *numPtr = &num;
    char *chPtr = &ch;

    void *ptr;

    ptr = numPtr;  // void 포인터에 int 포인터 저장
    chPtr = ptr;   // int 포인터를 void 로 받았는데 char 포인터로도 할당은 가능하다.

    printf("=> %d", *ptr); /*** WRONG (주의) void 포인터는 역참조 불가 ***/

    // void * 에 역참조를 사용하려면 원래의 포인터형으로 캐스팅해야 한다.
    int *intPtr = (int *)ptr; // void *ptr 을 int * 형으로 캐스팅
    printf("=> %d", *intPtr); // 이제부터 역참조 가능
    // => 10

    return 0;
}
⚠️ **GitHub.com Fallback** ⚠️