ubiquitous4g 2021. 5. 8. 15:53

포인터란?

- 해당되는 메모리 공간의 첫 바이트 주소값을 저장하는 변수이다.

포인터(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],