C에서의 포인터에 대한 포인터 (더블 포인터)
안녕하세요, 자신의 프로그래머가 되고자 하는 여러분! 오늘, 우리는 포인터의 세계로 흥미진진한 여정을 떠날 것입니다. 특히, 포인터에 대한 포인터에 대해 이야기할 것입니다. 지금, 여러분이 무엇을 생각할지 알아요: "포인터? 더블 포인터? 이건 꿈魘같네요!" 하지만 걱정마세요, 이것을 가능한 한 즐겁고 이해하기 쉽게 만들어드리겠습니다. 그럼, 좋아하는 음료수를 들고 편하게 앉아서, 함께 빠져들어봅시다!
C에서 더블 포인터는 무엇인가요?
숨겨진 보물을 찾는 모험을 상상해봅시다. 여러분에게 맵이 있습니다 (포인터라고 부르겠습니다). 그 맵은 보물 상자를 보여줍니다. 하지만 놀라운 일이 일어났습니다! 상자 안에는 또 다른 맵이 있습니다 (또 다른 포인터). 이것이 바로 더블 포인터의 본질입니다 - 또 다른 포인터를 가리키는 포인터입니다.
C 프로그래밍에서 더블 포인터는 그 이름 그대로 - 포인터에 대한 포인터입니다. 이는 또 다른 포인터의 주소를 저장하는 변수입니다. 처음에는 좀 혼란스러울 수 있지만, 걱정마세요, 점점 다루어 나갈 것입니다.
포인터에 대한 포인터 선언
더블 포인터를 선언하는 방법부터 시작해보겠습니다. 문법은 매우 간단합니다:
int **ptr;
여기서, ptr
는 정수에 대한 포인터에 대한 포인터입니다. 첫 번째 별표 *
는 이를 포인터로 만들고, 두 번째 *
는 이를 포인터에 대한 포인터로 만듭니다.
포인터에 대한 포인터 (더블 포인터) 예제
이를 더 잘 이해하기 위해 간단한 예제를 살펴보겠습니다:
#include <stdio.h>
int main() {
int x = 5;
int *p = &x;
int **pp = &p;
printf("x의 값: %d\n", x);
printf("p를 사용하여 x의 값: %d\n", *p);
printf("pp를 사용하여 x의 값: %d\n", **pp);
return 0;
}
출력:
x의 값: 5
p를 사용하여 x의 값: 5
pp를 사용하여 x의 값: 5
이를 분석해봅시다:
- 우리는 정수
x
를 선언하고 5로 초기화합니다. - 우리는
x
를 가리키는 포인터p
를 만듭니다. - 우리는
p
를 가리키는 더블 포인터pp
를 만듭니다. - 우리는 세 가지 다른 방법으로
x
의 값을 출력합니다:
- 직접
x
를 사용하여 - 단일 포인터
p
를 사용하여 (한 번 dereference하고*p
로) - 더블 포인터
pp
를 사용하여 (두 번 dereference하고**pp
로)
모든 세 가지 방법이 같은 값을 줍니다: 5. 다른 맵을 사용하여 보물에 도달하는 것과 같아요!
C에서 정상적인 포인터는 어떻게 작동하나요?
더블 포인터에 대해 더 깊이 들어가기 전에, 먼저 정상적인 포인터가 어떻게 작동하는지 빠르게 살펴보겠습니다:
int y = 10;
int *q = &y;
printf("y의 값: %d\n", y);
printf("y의 주소: %p\n", (void*)&y);
printf("q의 값: %p\n", (void*)q);
printf("q가 가리키는 값: %d\n", *q);
출력:
y의 값: 10
y의 주소: 0x7ffd5e8e9f44
q의 값: 0x7ffd5e8e9f44
q가 가리키는 값: 10
여기서, q
는 y
의 주소를 저장하는 포인터입니다. *q
를 사용하면, 우리는 그 주소에 저장된 값을 접근합니다.
더블 포인터는 어떻게 작동하나요?
이제 이를 더블 포인터에 확장해보겠습니다:
int z = 15;
int *r = &z;
int **rr = &r;
printf("z의 값: %d\n", z);
printf("z의 주소: %p\n", (void*)&z);
printf("r의 값: %p\n", (void*)r);
printf("r의 주소: %p\n", (void*)&r);
printf("rr의 값: %p\n", (void*)rr);
printf("r가 가리키는 값: %d\n", *r);
printf("rr가 가리키는 값: %p\n", (void*)*rr);
printf("rr가 가리키는 값의 가리키는 값: %d\n", **rr);
출력:
z의 값: 15
z의 주소: 0x7ffd5e8e9f48
r의 값: 0x7ffd5e8e9f48
r의 주소: 0x7ffd5e8e9f50
rr의 값: 0x7ffd5e8e9f50
r가 가리키는 값: 15
rr가 가리키는 값: 0x7ffd5e8e9f48
rr가 가리키는 값의 가리키는 값: 15
이것은 좀 혼란스러울 수 있지만, 이를 분석해보겠습니다:
-
z
는 값이 15인 정수입니다. -
r
는z
의 주소를 저장하는 포인터입니다. -
rr
는r
의 주소를 저장하는 더블 포인터입니다. -
*r
는z
의 값 (15)을 줍니다. -
*rr
는r
의 값 (즉,z
의 주소)을 줍니다. -
**rr
는z
의 값 (15)을 줍니다.
이렇게 생각해보세요: rr
는 r
를 가리키고, r
는 z
를 가리킵니다. 그래서 **rr
는 "첫 번째 포인터를 따라가고, 두 번째 포인터를 따라가서 그곳의 값을 줘"는 것과 같습니다.
더블 포인터는 정상적인 포인터처럼 행동합니다
여기 작은 비밀이 있습니다: 더블 포인터는 단순히 포인터입니다. 하지만 정수나 실수를 가리키는 것이 아니라, 또 다른 포인터를 가리킵니다. 이는 우리가 정상적인 포인터처럼 더블 포인터에도 같은 모든 것을 할 수 있다는 것을 의미합니다.
예를 들어, 우리는 더블 포인터를 배열과 함께 사용할 수 있습니다:
int main() {
char *fruits[] = {"Apple", "Banana", "Cherry"};
char **ptr = fruits;
for(int i = 0; i < 3; i++) {
printf("%s\n", *ptr);
ptr++;
}
return 0;
}
출력:
Apple
Banana
Cherry
이 예제에서, fruits
는 문자열을 가리키는 포인터의 배열입니다 (각각 ptr
는 fruits
의 요소를 가리킬 수 있습니다).
C에서 다단계 포인터 (삼중 포인터는 가능한가요?)
네, 단순히 삼중 포인터, 사중 포인터 등을 가질 수 있습니다! 이론적으로 인용의 수준에는 제한이 없습니다. 하지만 실제로는 더블 포인터 이상을 볼 수는稀有합니다.
여기 삼중 포인터의 예제입니다:
int x = 5;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
printf("x의 값: %d\n", ***ppp);
출력:
x의 값: 5
하지만 기억하세요, 할 수 있다고 해서 해야 한다는 것은 아닙니다. 여러 차례의 인용은 코드를 더 복잡하고 유지보수하기 어렵게 할 수 있습니다. 오래된 프로그래밍 관상에 따르면, "컴퓨터 과학의 모든 문제는 또 다른 인용 수준으로 해결할 수 있습니다... 그러나 너무 많은 인용 수준 문제는 아닙니다!"
결론
축하합니다! 여러분은 C에서 더블 포인터의 험한 물결을 성공적으로 건넜습니다. 기억하세요, 프로그래밍의 많은 개념처럼, 포인터에 대한 포인터는 처음에는 혼란스러울 수 있지만, 연습을 통해 자연스럽게 익숙해질 것입니다.
여기 우리가 다룬 주요 내용을 요약한 표가 있습니다:
개념 | 문법 | 설명 |
---|---|---|
정상적인 포인터 | int *p; |
정수를 가리킵니다 |
더블 포인터 | int **pp; |
정수에 대한 포인터를 가리킵니다 |
디리퍼런싱 | *p |
p 가 가리키는 값을 접근합니다 |
더블 디리퍼런싱 | **pp |
pp 가 가리키는 포인터가 가리키는 값을 접근합니다 |
주소 연산자 | &x |
x 의 주소를 가져옵니다 |
좋은 코드를 쓰고 있습니다!
Credits: Image by storyset