티스토리 뷰

이 글에서는 포인터에 대해 살펴보겠습니다. 포인터는 C 언어에서 제일 어려운 부분인데요, 사실 자바나 파이썬 같은 언어에는 포인터가 없기 때문에 C 보다 배우기 쉽습니다. 그만큼 포인터가 어려운 개념이지만 그래도 공부해야 하는 이유는 뭘까요? 포인터를 알면 프로그램이 메모리를 사용하는 방식을 이해하고 컨트롤할 수 있습니다. 그래서 알고리듬 시험이나 코딩테스트를 준비할 때 덮어두었던 C를 꺼내서 공부하고 C++을 새로 배우기도 합니다. 이들 언어는 포인터가 있어서 빠르고 메모리를 적게 사용하는 프로그램을 짤 수 있기 때문입니다.

그럼 이제 포인터에 대해 본격적으로 살펴보겠습니다. 포인터 다른 변수의 주소를 가지고 있는 변수입니다. 그럼 변수란 무엇인가? 변수란 메모리에 어떤 데이터를 저장할 수 있는 공간을 할당받아 값을 저장할 수 있는 것인데, 이름을 가지고 있습니다. 즉 이름을 붙인 메모리 공간이고, 그 공간은 메모리 내의 주소로 표시(구분)됩니다. 사실 이름은 우리가 주소 대신 편리하게 어떤 위치를 나타내기 위한 방법입니다. 그런데 그 주소라는 것도 결국 데이터이니까 우리가 주소록에 많은 주소를 저장하듯이 이런 변수의 주소도 어딘가 저장할 수 있겠지요. 주소를 저장해서 뭐에 쓸지는 나중에 생각해 보기로 하고 주소를 어떻게 가져오고 포인터에 저장하는지 살펴보겠습니다.

포인터 변수는 타입 다음에 별을 붙여서 int*, char* 같은 타입을 이용해 선언합니다. 그러면 그 타입의 변수의 주소를 저장할 수 있는 포인터가 생깁니다.

int a;
int* p;
p = &a;

그리고 a 변수의 주소 &a를 포인터 p에 지정했습니다. &는 변수에서 주소를 가져오는 주소연산자입니다.

변수 a는 어딘가 메모리에 할당될 것이고 그 주소는 그림에서는 26입니다. 그러면 포인터 p의 메모리에는 26이라는 값이 저장됩니다. 그리고 p는 변수 a 가리키는 포인터입니다. 우리는 p에 a의 주소를 저장했다. 또는 p가 a를 가리킨다라고 읽습니다.

주소는 항상 변수에서 얻어야 합니다. 프로그램에서 다른 주소를 쓸 일은 없습니다. 왜냐하면 프로그램이 자유롭게 사용할 수 있는 곳은 변수가 선언되면서 할당받은 메모리 영역 뿐이기 때문입니다. 모든 변수는 고유한 주소를 가집니다. (물론 이것은 프로그램이 실행 중일 때만 유효합니다. 프로그램이 실행을 끝내고 나면 메모리를 모두 내놓고 사라집니다)

지금까지 배운 내용을 요약해 보겠습니다.

  • 주소란? 변수가 가지고 있는(할당받은) 메모리 영역의 위치번호다
  • 변수는 선언되면서 메모리의 영역을 할당받는다
  • 모든 변수는 고유한 주소를 가진다
  • 포인터는 변수의 주소를 저장할 수 있는 변수다
  • 포인터는 반드시 변수의 주소로 값을 지정해야 한다

포인터를 이용하면 변수 이름이 없어도 변수를 접근할 수 있습니다. 포인터 pa의 주소를 가지고 있는데, *p는 그 주소가 가리키는 곳의 변수와 같습니다. *는 간접참조연산자라고 합니다. 프로그램의 어디서든 a를 알지 못하는 곳에서도 p만 알고 있다면 우리는 *p를 이용해서 a 변수가 하는 일을 대신할 수 있습니다. *pa는 같습니다.

다음 코드는 *p를 이용해서 포인터가 가리키는 변수의 값을 가져오거나 그 변수의 값을 바꾸는 것을 보여줍니다.

*p = 200;
b = *p + 3;

  • 위의 코드에서 *p 200으로 바꾸면 a의 값이 바뀌는 것을 알 수 있습니다.
  • 또한 *p의 값을 가져와서 3을 더해서 다른 변수 b에 저장할 수도 있습니다.
  • *p a가 가진 값을 읽어오고(get) a 값을 변경할 수 있습니다.(set)

포인터 p가 변수 a의 주소를 가지게 하기 위해 우리는 p = &a; 해서 & 연산자로 a의 주소를 가져왔습니다. &를 주소연산자라고 합니다.

반면 포인터에 *을 붙이면 그 포인터가 가리키는 곳의 변수가 됩니다. 이 때 * 간접참조 또는 역참조 연산이라고 합니다. 위의 그림에서 &a 26이라는 주소를 가지고 *p 200이라는 값을 가집니다.

int num;
ptr = #

[생각해 보기] 위 코드가 문제없이 잘 실행된다면 ptr의 타입은 무엇일까요?

더보기

포인터는 그것이 선언된 타입의 변수 주소만 가질 수 있습니다.  ptr int*로 선언되어야 int 변수 num의 주소를 가질 수 있는 것이죠. &를 하면 주소가 나오는데 그것은 int* 타입이 됩니다.

 

포인터의 지정은 일반 변수와 어떻게 다를까요? 다음 그림에서 두 가지를 비교하고 있습니다.

먼저 일반 변수의 지정을 생각해 봅시다. 변수 a와 변수 b는 각각 자신의 메모리를 가지고 있습니다. 그리고 b = a를 하면 b의 메모리에 a의 값을 저장하라는 뜻이 됩니다. 그래서 b30을 가지게 되었습니다.

포인터 변수의 지정도 같습니다. pa의 주소를 가지고 있는데, q = p; 하면 qa의 주소를 가지게 됩니다. 이것을 그림으로 나타내면 이것과 같습니다. p, q 두 포인터가 같은 변수를 가리키고 있습니다.

포인터는 선언할 때 어떤 타입의 변수의 주소를 가질 것인지 정해야 합니다. int*로 선언된 포인터는 int 형 변수의 주소만 가질 수 있습니다. int형 변수만 가리킬 수 있습니다.

포인터의 주소는 항상 변수에서 가져온 주소여야 한다고 했죠? 그러니까 C 컴파일러는 포인터에 주소를 넣을 때 같은 타입의 변수의 주소인가를 검사합니다. 다음 그림에는 세 가지 다른 타입의 포인터가 선언되어 있습니다.

예를 들어 *pf = value;라고 하려면 value의 타입은? pffloat*이므로 valueflaot여야 이렇게 지정할 수 있겠지요? 또 그래야 우리가 *pf를 사용할 때 value와 같은 것 즉 float 가 될 수 있습니다.

틀린 타입을 지정하면 어떻게 될까요? 지정에서 컴파일 오류가 발생합니다. 그런데 한가지 주의할 것이 키보드에서 데이터를 입력할 때 scanf에서 우리는 주소 연산자를 이용해 변수의 주소를 보냅니다. 이때 “% “ 부분의 타입과 변수의 타입이 같아야 이 주소를 가져가서 잘 처리할 수 있습니다. 이 때 타입이 틀리면? 여러 가지 방식의 오류가 발생합니다. 잘못된 값이 들어가기도 하고 프로그램이 종료하기도 합니다. 이것은 실제 문제가 발생했을 때 더 설명하도록 하겠습니다.

'C 프로그래밍기초' 카테고리의 다른 글

진짜 이차원배열처럼 malloc하는 방법  (0) 2020.03.14
구조체란 무엇인가?  (0) 2020.02.11
C printf 요약매뉴얼  (0) 2020.01.08
C from Python - step 1  (0) 2019.12.25
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함