C & C++

[C언어] 포인터, 이중포인터 개념, 실습 예제 완벽 이해

jimmy_AI 2021. 12. 7. 12:05
반응형

안녕하세요.

 

이번 글에서는 많은 C언어를 공부하시는 분들을 애먹이는 개념이지만,

컴퓨터 구조의 기초, 메모리 할당과 밀접한 연관이 되어있으며,

이후에 다룰 매우 중요한 개념인 배열의 근간이 되는

포인터, 이중포인터의 실습 예제에 대해서

최대한 쉽고 친근하게 다루어보도록 하겠습니다! 

C언어 포인터 선언(& 과 *의 차이)

먼저, 포인터를 선언하는 예시입니다. 여기서는 int 자료형을 예제로 보여드렸지만,

double, char 자료형 등 기본 자료형이라면 모두 포인터를 선언할 수 있습니다.

#include <stdio.h>

int main(){

int a = 3;
int *b = &a;

printf("a의 값 = %d\n", a);
printf("b의 값 = %p\n", b); // %p로 포맷팅
}

여기서 *와 &의 차이에 대해서 혼동하시는 경우가 많이 있는데요,

 

*'포인터를 선언'하겠다는 의미와, '해당 포인터가 가리키는 원래 값을 가져올게요'

두 가지 의미로 해석해주시면 됩니다.

참고로, int* b, int *b 처럼 *의 위치는 int와 b 사이라면 상관이 없습니다.

 

반면, &*와는 반대 방향이라고 생각해볼 수 있습니다.

& '그 변수가 가리키던 주소의 위치를 받겠습니다.' 라는 의미입니다.

 

위에서 a는 일반적인 int 형 변수, b는 a의 주소를 가리키는 포인터가 되는데요,

한번 두 print 결과를 비교해보겠습니다. 포인터를 출력할 때는 %p로 포맷팅해주세요.

확인해보니 a에는 3이 담겨있지만,

b에는 알 수 없는 이상해보이는 문자들이 담겨있습니다.

이 것은 이상한 문자는 아니고요, 메모리 주소를 나타내는 형식 정도로 이해해주시면 됩니다.

 

C언어 포인터 저장 값 접근(* 이용)

이번에는 C언어 포인터 저장 값에 접근하는 방법을 알아보겠습니다.

#include <stdio.h>

int main(){

int a = 3;
double ad = 3.0;

int *b = &a;
double *bd = &ad;

printf("b가 가리키는 값 = %d\n", *b); // *를 앞에 붙여서 값 가져오기
printf("b의 값 = %p\n", b);

printf("bd가 가리키는 값 = %.1f\n", *bd); // *를 앞에 붙여서 값 가져오기
printf("bd의 값 = %p\n", bd);
}

이번에는 double 변수와 그 변수를 가리키는 포인터를 1개 더 선언을 해보았습니다.

 

b, bd는 포인터이고, a, ad는 일반 변수임에 주목해주세요.

b, bd에 저장된 값을 가져오고 싶을 때는, 프린트 하는 곳에서 변수 앞에 *를 붙여서

값을 가져오는 과정만 기억해주시면 됩니다!

 

위의 결과를 확인해보겠습니다.

*를 통해서 포인터가 가리키는 값을 불러오는 과정이 잘 실행됬음을 확인할 수 있네요!

 

C언어 이중 포인터 선언, 예시

이중 포인터도 어렵게 생각하실 필요는 없습니다.

포인터를 지목하고 있는 포인터 정도로 생각하시면 되는데요,

다음 예제를 통해서 이해를 해보도록 하겠습니다.

반응형
#include <stdio.h>

int main(){

int a = 3;
int* b = &a;
int** c = &b; // 포인터 변수를 가리키는 변수

printf("a에 저장된 값 = %d\n", a);
printf("b에 저장된 값 = %p\n", b);
printf("c에 저장된 값 = %p\n", c); // 똑같이 %p로 포맷팅

printf("b가 가리키는 값 = %d\n", *b);
printf("c가 가리키는 값 = %p\n", *c); // 여전히 포인터라 %p
printf("c가 최종적으로 가리키는 값 = %d\n", **c); //**로 두번 접근
}

이중 포인터의 선언은 *를 두개 붙여서 **로 선언을 해주면 됩니다.

c가 int형 자료를 가리키는 이중 포인터 변수로 선언이 되었는데,

위에서 말한 *의 원리를 살펴본다면,

*c는 b를 가리키는 상황이 되어 여전히 포인터가 될 것이고,

**c는 *b를 가리키는 상황이 되어 최종적인 정수형 자료 a에 접근이 되는

것으로 이해해볼 수 있을 것 같습니다. 프린트 결과는 아래를 직접 보시죠!

 

이중 포인터 사용 이유

그렇다면 복잡하게 이중 포인터를 사용하는 이유가 무엇일까요?

다음 번에 다룰 배열의 원리를 이해하시면 사용 이유를 짐작하실 수도 있는데요.

 

간단하게만 말씀드리자면, 만일 배열에 10개의 원소를 저장한다고 하면,

메모리 주소를 1칸(변수가 차지하는 사이즈, ex. int -> 4 byte)씩 옮겨서

순서대로 10칸에 원소를 저장하여 접근하는 방식입니다.

 

swap 함수에서 두 변수를 실제로 바꾸려면 메모리 주소를 바꿔주는 원리를 이용한다면

포인터를 사용하는 것이 편리하게 됩니다.

 

그렇다면, 만일 두 변수에 저장된 배열을 통째로 바꾸고 싶다면?

이런 경우에 이중 포인터를 사용하는 것이 필요하게 되는 것입니다.

 

물론, 3중 포인터 등도 구상은 해볼 수 있으나, 실제로는 거의 사용할 일이 없어

이중 포인터 정도까지만 예제를 잘 이해해주시면 좋을 것 같습니다.