int main (void)
{
int num = 7;
int *pnum; // 포인터 변수 pnum 선언
pnum = # // num의 주솟값을 포인터 변수 pnum에 저장
}
// pnum에는 num의 주솟값이 할당되어 있다.
// 이를 아래와 같이 출력할 수 있다.
printf("%p",pnum);
// 포인터 pnum을 가지고, 변수 num 값에 접근할 수 있다.
// 이를 '역참조'라고 하며, 다음과 같이 * 기호를 이용할 수 있다.
printf("%d",*pnum);
위에서 보는 것처럼, pnum으로 변수 num의 주소를 가지고 있으면, 포인터 pnum을 가지고 '*' 기호를 통해 변수 num값에 접근할 수 있다. (역참조)
c에서 포인터의 개념은 이렇듯 해당 주소에 존재하는 값을 역참조 하여 값을 변경하는 등의 동작에 이용된다.
포인터는 왜/어디에 쓰는걸까?
C를 통해 알고리즘, 자료 구조 등을 구현 해보면 필요성을 느낄 수 있을 것이다.
'포인터의 개념 자체'가 어렵지는 않다.
- 다만, (포인터 + 함수), (포인터 + 배열), (포인터 + 구조체) 등 다른 개념과 포인터가 혼용되기 시작하면 그 때부터 어렵게 느껴지기 시작한다.
- 필자의 Tip : 작은 개념부터 천천히 확장해가며 이해하는 것이 개인적인 팁.
arr[i] = *(arr+i)
위의 두 표현은 같은 표현이다.
void showArrayElem (int *param, int len)
void showArrayElem (int param[], int len)
void increment(int x){
x += 1; //원본 값은 변경되지 않음.
}
int main () {
int a = 10;
increment(a); // 함수 호출해도 a의 값은 여전히 10
return 0;
}
void increment (int* x) {
*x += 1; // 포인터를 통해 원본변수의 값을 변경.
}
int main () {
int a = 10;
increment(&a); // 'a'의 값은 11로 변경 됨.
return 0;
}
const int* ptr = # // 포인터 '참조 대상' 변경 불가
int* const ptr = # // '포인터 변수'의 상수화 → 할당된 주소 값 변경 불가.
이중 포인터는 단일 포인터를 한 번 더 포인터로 지칭하는 wrapping 일 뿐, 어렵게 생각하지 말자.
아래 예제 코드를 보고 이해할 수 있으면 된다.
int main () {
double num = 3.14;
double *ptr = #
double **dptr = &ptr;
double *ptr2;
printf("%9p %9p \n",ptr,*dptr);
printf("%9g %9g \n",ptr,**dptr);
ptr2 = *dptr;
*ptr2 = 10.99;
printf("%9g %9g \n",num, **dptr);
return 0;
}
<실행 결과>
0032FD00 0032FD00
3.14 3.14
10.99 10.99
int main () {
double num = 3.14;
double *ptr = #
double **dptr = &ptr;
double *ptr2;
}
동적 할당 개념을 살펴보기 전에, C언어의 메모리 구조를 간단히 살펴보자.
코드 영역: 프로그램 코드 자체를 저장하는 영역
데이터 영역: 전역 변수, static 변수 등 프로그램 종료 시까지 남아있는 데이터들을 저장하는 영역
힙 영역: 작성자가 데이터를 원하는 시점에 생성 / 소멸시킬 수 있는 영역.
→ "동적 할당에 이용하는 영역"
스택 영역: 특정 함수에서 사용되는 지역 변수, 매개변수 등
함수를 빠져나가면 삭제되는 데이터를 저장하는 영역.
동적 할당의 개념을 이해하기 위해 주목해야 할 메모리 영역은 '힙 영역'이다.
#include <stdlib.h>
int *ptr1 = (int*)malloc(sizeof(int));
free(ptr1);
C언어에서는 malloc( ), calloc( ) 등을 이용해 힙 영역에 원하는 크기만큼의 메모리를 할당하고, 원하는 시점에 free( )를 호출 해 이 영역을 소멸시킬 수 있다.
위처럼 동적 할당을 이용해 할당한 메모리 공간은 보통 포인터로 연결하고, 역참조를 통해 원본 값을 접근하며 사용한다.
#include <stdlib.h>
// int형 5개 만큼의 메모리 공간 할당, 0으로 초기화
int *arr = (int*)calloc(5,sizeof(int));
calloc( )은 malloc( )의 사촌 뻘 되는 함수로, 동작 자체는 malloc과 같이 동적 할당한 메모리를 반환해주는 동작을 한다.
차이점이 있다면, 우선 인자를 두 개를 받는다. 할당할 메모리의 블럭 개수와 블럭의 사이즈를 위와 같이 전달 받는다.
calloc은 자동으로 할당한 메모리의 초기값들을 0으로 초기화 해준다.
// 길이가 7인 int형 배열로 확장
arr = (int*)realloc(arr,sizeof(int)*7);
동적 할당을 사용하는 이유?
- 동적 할당을 이용한 메모리 공간의 생성/소멸 시점을 작성자가 조절할 수 있다.
- 크기가 가변적인 배열의 선언을 구현할 수 있다.
이 포스팅은 필자가 개념을 돌아보기 위해 요점만 가볍게 작성하였으며, 디테일 한 설명은 부족할 수 있습니다.