포인터란?
- 해당되는 메모리 공간의 첫 바이트 주소값을 저장하는 변수이다.
포인터(pointer) 논리적으로 "☞ 메모리 공간을 가리킨다" 는 의미다.
포인터를 통한 주소값 OS가 관리하는 가상의 메모리 주소값이다.
고로, C에서 메모리가 어떻게 구성, 관리되는지 이해하면 포인터를 이해하는 것이다.
- *포인터 연산자의 위치는 상관 없으나, 변수명 앞에 *를 사용한다. (int* a, b, c 문제)
포인터와 메모리
컴파일된 C 프로그램은 세 종류의 메모리를 사용한다.
- 정적(Static)/전역(Global)
- 동적(Dynamic)
- 자동(Autmatic)/로컬(Local)
포인터 * 참조 연산자
* 는 주소값을 참조하는 연산자다.
int형 변수 x 를 사용하면 13을 가져오듯이, 포인터 변수 xp 를 사용하면, 0x0012FF44를 가져온다.
포인터 변수 xp로 얻게된 주소값에, * 참조 연산자를 사용하면, 주소값을 참조해 값을 가져온다.
NULL 포인터
- 포인터에 NULL이 할당되면, "아무것도 가리키지 않음"을 뜻하는 널포인터 이다.
- 초기화되지 않는 포인터와 널포인터는 같지 않다.
- NULL은 0으로 해석되고 포인터에 0 할당도 가능하다.
- 고로, NULL은 포인터가 아닌 상황에서 사용하면 안된다.
- 전역, 정적 포인터는 자동 NULL 초기화된다.
//NULL의 실체 define NULL ((void *) 0)
if(pi){ //if(pi != NULL), if(pi != 0)
// 널이 아닌 경우
}
else{ //if(pi == NULL), if(pi == 0)
// 널인 경우
}
- NUL 아스키('\0')와 다르다.
int num
int *pi = 0 // NULL 포인터
pi = &num
*pi = 0 // 정수 0
void 포인터
- 어떤 타입의 데이터도 참조할 수 있지만, 포인터 연산이나 메모리 참조와 같은 작업은 할 수 없다.
- 모든 포인터는 void 포인터에 할당 가능하고, 다시 원래 타입으로 케스팅 가능하다.
- 다른 타입으로 케스팅 되더라도 막을 방법이 없다....
- 함수 포인터에 사용될 수 없다.
- void 포인터를 사용할 때에는 반드시 명시적 케스팅 후에 사용해야 한다.
int num = 10; // 변수 선언
void* ptr_num = # // void 포인터 선언
printf("변수 num가 저장하고 있는 값은 %d입니다.\n", num);
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
*(int*)ptr_num = 20; // void 포인터를 통한 메모리 접근
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
포인터 연산 - 포인터에 정수 더하기, 빼기, 서로 다른 두 포인터 빼기, 비교
포인터 + 정수 = 포인터 + (정수 x 포인터 데이터 타입 크기(Byte))
int *ptr + 1 = ptr + (1 x 4Byte)
short *ptr + 1 = ptr + (1 x 2Byte)
char *ptr + 1 = ptr + (1 x 1Byte)
void *ptr + 1 = ptr + (1 x 1Byte) // 오류, 캐스팅 후 연산 가능 (int *)ptr + 1
포인터의 크기와 데이터 타입
- 포인터 크기는, 장비와 컴파일러에 따라 다르므로 확인이 필요하다.
- 일반적인 포인터 크기: 4Byte, 16진수 8자리(0x00000000 ~ 0xFFFFFFFF)
상수와 포인터
포인터 변수와 주소값, 데이터값의 이해
포인터 배열
02.4.1. 포인터 배열의 개념
- 포인터 변수의 집합
- 값으로 변수의 주소값을 가진다.
int x = 10, y = 20, z = 30;
int* ptr_arr[3] = {&x, &y, &z};
printf("%d %d %d\n", x , y, z);
printf("%d %d %d\n", *ptr_arr[0], *ptr_arr[1], *ptr_arr[2]);
// ↑10 , ↑20, ↑30,
printf("%p %p %p\n", &x, &y, &z);
printf("%p %p %p\n", ptr_arr[0], ptr_arr[1], ptr_arr[2]);
// ↑0x72, ↑0x85, ↑0x92,
printf("%p %p %p\n", &ptr_arr[0], &ptr_arr[1], &ptr_arr[2]);
// ↑0x10, ↑0x14, ↑0x18,
printf("%p %p %p\n", &ptr_arr[0], ptr_arr , &ptr_arr );
// ↑0x10, ↑0x10, ↑0x10,
// int **, int **, int *(*)[3],