앞에서 간간히 포인터를 사용했습니다.
오늘은 포인터와 다중 포인터에 대해 알아보겠습니다.
포인터는 메모리의 주소를 저장하는 변수입니다. 메모리에 있는 데이터는 결국 메모리 주소에 저장이 되기 때문에 직접적인 접근이 가능해집니다.
포인터의 선언은 다음과 같습니다.
자료형 *변수이름;
예제로 살펴보겠습니다.
#include <stdio.h>
int main(void) {
int num = 50;
//&는 주소 연산자로 변수의 이름을 받으면 해당 변수의 주소를 반환합니다.
printf("num의 값 = %d \n", num);
printf("num의 주소는 = %p \n", &num);
int *p_num = #
printf("*p_num의 값 = %d \n",*p_num);
printf("*p_num에 저장된 주소는 = %p \n", &*p_num);
printf("*p_num에 저장된 주소는 = %p \n", p_num);
printf("\n\n*p_num의 값을 변경 50->100\n");
*p_num = 100;
printf("*p_num의 값 = %d \n", *p_num);
printf("num의 값 = %d \n", num);
return 0;
}
int num = 50;을 선언 후
int *p_num = # -> 인트형 포인터 변수를 선언 후 &num을 대입해줍니다.
&는 주소 연산자로 &num은 num은 메모리 주소를 *p_num에 저장한다는 의미입니다.
결과를 보면 두 변수의 주소를 보면 같은 모습을 보여줍니다.
포인터는 *(역참조 연산자)을 붙이냐 안붙이냐에 따라서 반환되는 값이 달라집니다.
포인터 변수에 저장된 주소의 값을 가져오려면 *를 붙이면 됩니다. ex)*p_num
포인터 변수에 저장된 주소를 가져오려면 *를 붙이지 않습니다. ex)p_num
*을 붙인 상태에서 꼭 주소를 보고 싶다면 &를 통해서 볼 수 있습니다. ex) &*p_num
포인터 변수는 주소를 저장하기 때문에 주소에 직접적인 접근이 가능해집니다.
포인터 변수에서 저장된 주소의 값을 변경하면 원래의 주소의 변수의 값도 같이 변경이 됩니다.
지금까지는 포인터에 변수의 주소를 저장했습니다.
포인터에 직접 주소를 입력도 가능은 합니다.
#include <stdio.h>
int main(void) {
int *p_num=10000;
printf("*p_num의 주소는 = %d \n", p_num);
return 0;
}
에러는 발생하지 않습니다. 하지만 보통은 특수한 경우를 제외하고는 사용하지 않습니다.
왜냐하면 이 주소가 어떤게 사용되고 있던 건지 명확하지 않기 때문에 잘 사용하지 않습니다.
버퍼를 직접 할당해야 하는 경우에 많이 사용합니다.
만약에 사용하더라도 다음처럼 고쳐서 사용하는 게 좋습니다.(형식을 명확히)
int *p_num=10000; -> int *p_num=(int *)10000;
포인터도 const가 사용 가능합니다. const는 보통 상수를 만들때 사용합니다.
포인터는 const의 위치에 따라 조금씩 영향력이 다릅니다.
#include <stdio.h>
int main(void) {
int num = 100;
int num2 = 200;
const int *p_num = #
int * const p_num2 = #
p_num = &num2; // 참조된 변수의 값을 변경이 불가능
*p_num2 = 500; // 저장된 주소 변경 불가능
return 0;
}
*앞에 const가 붙으면 저장된 주소의 값을 변경이 불가능합니다.
*뒤에 붙게되면 포인터에 저장된 주소를 변경이 불가능합니다.
다음으로 다중 포인터입니다.
포인터도 결국 변수와 비슷한 개념이기 때문에 주소가 존재합니다.
포인터의 주소는 포인터의 포인터(pointer to pointer : 이중 포인터)를 이용해 저장해야 합니다.이런 식으로 *를 몇 개 붙이냐에 따라 삼중, 사 중등 계속해서 포인터가 사용이 가능합니다. 이를 다중 포인터라고 합니다.
예제로 이중 포인터를 살펴보겠습니다.
#include <stdio.h>
int main(void) {
int num = 10;
int *p_num = #
int **pp_num = &p_num;
printf("num의 주소는 = %p \n", &num);
printf("p_num에 저장된 주소는 = %p \n", p_num);
printf("pp_num에 저장된 주소는 = %p \n", pp_num);
printf("*pp_num에 저장된 주소는 = %p \n", *pp_num);
printf("\n\n **pp_num = 100 \n");
**pp_num = 100;
printf("num의 값 = %d \n", num);
return 0;
}
이중 포인터의 개념 보자면
이중 포인터는 *(역참조 연산자)이 붙는 거에 따라서 값이 달라집니다.
**pp_num는 *이 두 번 붙었기 때문에 num -> p_num -> pp_num로 역 참조하여 값을 가져오게 됩니다.
*pp_num는 *p_num와 같습니다. 역참조를 한번 사용했기 때문에 p_num -> pp_num가 됩니다.
다중 포인터는 *에 따라서 원하는 값을 가져올 수 있습니다.
다중 포인터는 매개변수에서도 사용할 수 있습니다.
다중 포인터를 매개변수로 사용할 때는 포인터를 인자로 받을 때 많이 사용합니다.
예제를 통해 포인터와 이중 포인터의 매개변수의 차이를 보면
포인터 변수 인자를 포인터 매개변수로 받을 경우
#include <stdio.h>
#include <stdlib.h>
void fun_1(int *p) {
p = malloc(sizeof(int));
}
int main(void) {
int *p_num = NULL;
fun_1(&p_num);
*p_num = 100;
printf("p_num의 값 = %d \n", *p_num);
free(p_num);
return 0;
}
함수 내에서 임의로 크기로 할당해도 함수 밖으로 나가면 크기가 사라짐으로 포인터에 할당된 크기가 없기 때문에 애러가 발생합니다.
하지만 이중 포인터 매개변수로 포인터 인자를 받으면
#include <stdio.h>
#include <stdlib.h>
void fun_2(int **pp) {
*pp = malloc(sizeof(int));
}
int main(void) {
int *p_num_2 = NULL;
fun_2(&p_num_2);
*p_num_2 = 100;
printf("p_num_2의 값 = %d \n", *p_num_2);
free(p_num_2);
return 0;
}
포인터 인자에 접근해서 크기를 할당하게 됨으로 함수 밖으로 나가도 할당된 메모리가 남게 됩니다.
그래서 값을 저장할 수 있습니다.
'공부 자료실 > C언어' 카테고리의 다른 글
C언어 - #define (0) | 2020.07.04 |
---|---|
C언어 - void 포인터 (0) | 2020.07.03 |
C언어 - typedef (0) | 2020.07.02 |
C언어 - 열거형 (0) | 2020.07.02 |
C언어 - 공용체 (0) | 2020.07.01 |
댓글