C 언어 변수, 포인터, 이중 포인터, 포인터 배열 정리
C 언어에서 변수는 메모리 공간이라는 관점으로 보고, 포인터와 이중 포인터, 포인터 배열의 의미를 정리한다.
변수의 의미
C 언어에서 변수는 단순한 이름표가 아니라, 값을 저장하기 위한 메모리 공간 자체로 볼 수 있다.
- 변수를 선언하면 메모리 공간이 생성된다.
- 그 공간에 정수, 문자, 구조체 같은 실제 값이 저장된다.
예를 들어:
int a = 5;
위 코드는 a라는 이름의 정수형 메모리 공간을 만들고, 그 안에 5를 저장한다.
포인터
포인터는 주소를 저장하기 위한 변수다.
& 연산자
&는 어떤 변수의 주소를 구하는 연산자다.
int a = 5;
printf("%p", (void *)&a);
이 코드는 변수 a가 저장된 메모리 주소를 출력한다.
*의 두 가지 의미
*는 문맥에 따라 의미가 달라진다.
- 선언에서의
*포인터 변수를 선언할 때 사용한다.
int *p = &a;
위 코드는 int를 가리키는 포인터 변수 p를 선언하고, a의 주소를 저장한다.
- 사용에서의
*역참조를 할 때 사용한다.
printf("%d", *p);
p 안에 들어 있는 주소를 따라가서, 그 위치에 저장된 실제 값을 읽는다.
역참조
역참조는 주소를 담은 변수에서, 그 주소가 가리키는 실제 메모리 공간으로 다시 찾아가는 동작이다.
int a = 5;
int *p = &a;
printf("%d\n", *p);
결과:
5
흐름은 이렇게 이해하면 된다.
p에는a의 주소가 저장되어 있다.*p는 그 주소로 이동한다.- 그 주소에 저장된 실제 값
5를 읽는다.
이중 포인터
이중 포인터는 포인터 변수의 주소를 저장하기 위한 포인터다.
int a = 5;
int *p = &a;
int **p2 = &p;
각 변수의 의미는 다음과 같다.
a는 정수 값을 저장하는 변수p는a의 주소를 저장하는 포인터p2는p의 주소를 저장하는 이중 포인터
즉, 저장 관계를 보면 다음과 같다.
a <- 실제 정수 값 저장
p <- a의 주소 저장
p2 <- p의 주소 저장
별의 개수와 저장 대상
저장하려는 대상보다 *가 하나 더 많아야 그 주소를 저장할 수 있다.
int a = 5;
int *p = &a;
int **p2 = &p;
int ***p3 = &p2;
p는a를 가리킨다.p2는p를 가리킨다.p3는p2를 가리킨다.
즉:
*p는a**p2는a***p3는a
처럼 더 깊게 역참조할 수 있다.
포인터 배열
포인터 배열은 포인터 변수를 여러 개 배열 형태로 관리하는 구조다.
예를 들어 다음 두 코드는 비슷하게 보일 수 있다.
int *p1, *p2, *p3, *p4, *p5;
int *p[5];
둘 다 int를 가리키는 포인터 5개를 만든다는 점에서는 비슷하지만, 자료구조는 다르다.
- 첫 번째는 서로 독립적인 포인터 변수 5개
- 두 번째는 포인터를 원소로 가지는 배열 1개
일반 배열과 포인터 배열 비교
일반 정수 배열은 값을 직접 저장한다.
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[0]);
반면 포인터 배열은 주소를 저장한다.
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int *arr[5] = {&a, &b, &c, &d, &e};
printf("%d\n", *arr[0]);
여기서:
arr[0]은a의 주소*arr[0]은 그 주소에 저장된 실제 값1
즉 포인터 배열의 값을 사용할 때는, 주소만 보고 싶다면 arr[i], 실제 값을 읽고 싶다면 *arr[i]처럼 역참조해야 한다.
포인터가 가리킨다는 의미
포인터가 어떤 대상을 가리킨다는 것은, 그 변수가 특정 메모리 주소를 저장하고 있고, 그 주소를 통해 실제 데이터에 접근할 수 있다는 뜻이다.
즉:
- 포인터는 주소를 저장한다.
- 역참조는 그 주소를 따라가 실제 값에 접근한다.
- 이중 포인터는 포인터의 주소를 저장한다.
- 포인터 배열은 여러 주소를 배열 형태로 관리한다.
정리
- 변수는 값을 담는 메모리 공간이다.
- 포인터는 주소를 저장하는 변수다.
&는 주소를 구한다.*는 선언에서는 포인터 타입을 뜻하고, 사용에서는 역참조를 뜻한다.- 이중 포인터는 포인터의 주소를 저장한다.
- 포인터 배열은 여러 포인터를 배열로 묶어 관리한다.
처음에는 *와 &가 헷갈리지만, “값”, “주소”, “그 주소를 다시 따라가는 것”을 구분해서 보면 훨씬 이해하기 쉬워진다.