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 = # // 포인터 ptr 을 선언하고 num 의 주소 담기
printf("=> %d\n", *ptr); // 포인터역참조. ptr이 가리키는 주소의 실제값
// => 10
int *numPtr; // 포인터 numPtr을 선언하기
numPtr = # // 포인터 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 = #
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 *포인터이름;
자료형이 정해지지 않은 포인터이다. 어떤 자료형이든 지정할 수 있으며 컴파일러 경고도 없다. 이러한 특성으로 인해 역참조할 수 없다.
#include <stdio.h>
int main(int argc, char *argv[]) {
int num = 10;
char ch = 'a';
int *numPtr = #
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;
}