TL;DR
포인터는
메모리의 주소값을 저장하는 변수를 일컫는 개념임.
그냥 일반 데이터가 아니고,
주소값을 저장하다보니
데이터 다루는 데는 없는
이런 저런 낯선 기능들이 있는것.
그건 익히면 된다.
결론,
겁먹지 말자.
포인터는 그냥 변수다.
포인터가 어렵다는 소문을 듣고
C를 공부해서 그런지
포인터 글자만 봐도
두려움이 생겼다.
이해한 것 같으면
모르고 있는 나를 발견하는 날의 연속이었다.
책도 여러번 읽고,
chatGPT와 깊은(?) 대화도 나누고,
유튜브에 올라온 한국기술교육대학교의 김덕수 교수님 C언어 Lv.2 강의도 쭉 보고,
똑똑한 팀원에게 쪽집게 교습도 받고,
RBTree 구현한 코드도 분석하며,
포인터를 연구한 결과!
내가 어떤 부분을 이해못해 헤매고 있었는지
깨닫게 되었고,
개념을 다시 정리할 수 있게 되었다.
부디 나와 같은 지점에서 헤매는 이에게
도움이 되길 바란다.
그리고 위의 김덕수 교수님 강의를 강추한다.
C는 컴퓨터 메모리를
효과적으로 관리할 수 있는 언어이다.
C는
효율적 메모리 관리를 위해
크게 두가지를 중요한 정보로 다룬다.
첫번째, 자료형.
데이터의 형식에 따라
메모리를 차지하는 크기가 달라지기 때문이다.
두번째, 메모리 주소값.
데이터가 너무 크거나, 복잡한 형태이면
그것을 사용할 곳마다 다 복제해서 쓸텐데,
그럼 복제할 때마다 메모리를 너무 많이 잡아먹게되니
효율이 너무 떨어진다.
그래서 그 원본 데이터을 전부 복제하지 않고,
그 데이터가 저장되어 있는 메모리의 주소만 가지고 다니면서
이 함수, 저 함수에서 값을 연산하는 방법을 고안해내게 됐고,
아... 이 주소값만 담아서 가지고 다닐 수 없을까...?
하다가 나온 개념이 바로 포인터이다.
포인터는
주소를 담아 가지고 다니는 변수를
부르는 말이다.
그런데
주소만 있으면 뭐하는가?
우리는
그 주소에 저장된 실제 데이터가
최종적으로 필요한 것인데 말이다.
그래서 이 포인터는
본인의 메모리에 저장하고 있는 주소값을 활용할 수 있는 기능을 갖고 있다.
다시 말해,
이 포인터를 C언어의 문법대로 조작하면,
그가 갖고 있는 메모리 주소에 접근하여,
그 주소에 저장되어 있는 실제 데이터를 확인할 수 있게 된다는 말이다.
자,
포인터의 존재 이유를 이제 알았다.
이제 활용만 하면 된다.
활용 방법도 어려울 것 없다.
포인터의 존재 이유에 따른 기능을
핵심적으로 이해하면 된다.
1. 포인터라는 변수를 어떻게 선언하는가?
2. 포인터라는 변수에 주소를 어떻게 저장하는가?
3. 포인터라는 변수에 저장된 주소를 어떻게 활용해서 원본 데이터를 확인하는가?
이게 다다.
1. 포인터 선언하기
`int *ptr;`
일반적인 자료형의 변수를 선언할 때는
`int age;`
1. 본인이 어떤 자료형인지 알려주고
2. 본인을 어떻게 부를지 이름을 써주면 됐지만
포인터는
1. 포인터가 저장하고 있는 주소로 찾아갔을 때의 원본 데이터가 어떤 자료형인지 알려주고
2. 본인이 포인터 역할을 하는 변수라고 *로 표시해서 알려주고
3. 본인을 어떻게 부를지 이름을 써줘야 한다.
왜 이렇겠는가?
포인터 자신은 그저 주소 캐리어일 뿐이고,
실제 그 메모리 주소에 담긴 데이터가 주인공이기 때문에,
포인터를 선언해줄 때도, 주인공의 데이터 형을 알려주는 거다.
그리고 얘는 포인터라고 *로 구분해서 알려주는 거다.
2. 포인터에 데이터의 메모리 주소 저장하기
우리가 포인터에 저장할 것은 무엇인가?
실제 데이터가 저장된 메모리의 주소값이다.
그럼 그 주소는 어디서 불러오는가?
실제 데이터가 저장된 변수로부터 불러올 수 있다.
데이터를 담은 변수에서 어떻게 주소를 불러오는가?
`&변수이름`
이렇게 변수 이름 앞에 & 기호를 붙이면,
그 변수에 할당된 메모리의 주소값이 불러와진다.
예를들어,
age라는 변수에 저장된 데이터의 메모리 주소를 캐리할
agePtr이라는 포인터를 쓴다면?!
int age = 33;
// 방법 1.
int *agePtr = &age;
// 방법 2.
int *agePtr;
agePtr = &age;
위와 같이
1. 포인터를 선언할 때 주소값까지 담아버리든지,
포인터는 미리 선언해두고,
2. 나중에 그 포인터 변수에 다시 주소를 담던지
하면 포인터 변수의 메모리 공간에는
어떤 변수의 '메모리 주소값'이 저장된다!
(참고 사항)
개발을 더 하다보면 배열 데이터를 다루게 되는데,
이 배열 데이터를 담은 변수는,
그 자체가 포인터처럼 작동한다.
`int arr[2] = {1, 2};`
`int arrPtr = arr;`
그래서 위와 같이 &를 붙이지 않는 모습을 볼 수 있다.
왜 안 붙이겠는가?
포인터변수에 &를 붙여버리면,
그 포인터변수가 담고있는 다른 변수의 주소가 아니라
본인이 저장된 메모리 주소를 뱉을 것 아닌가.
내가 궁금한 건 그게 아닐진데..
근데 arr 본인이 포인터 변수 자체니까,
본인이 그 데이터의 주소값을 갖고 있지 않겠음?
그니까 그냥 &없이
바로 arrPtr에게 주소값을 줘버리는 것이지.. ok?
자, 이제 대망의!
가장 중요한!!
3. 메모리의 주소로 찾아가, 그곳에 저장된 데이터 확인하기
데이터가 저장된 메모리의 주소는 어디에 있는가?
포인터 변수에 담아 놨다.
좋다. 그럼 포인터 변수를 조작하면 된다.
`*agePtr`
이렇게 포인터 변수 앞에 *을 붙이면
그 포인터에 저장된 주소로 찾아가서 원본 데이터를 불러오게 된다.
이게 끝이다.
여기서 주의할 것!!
포인터 변수를 선언하면서 썼던 *의 기능과는 완전히 다르다.
이미 agePtr라는 변수는
앞에서 선언하면서 포인터 변수임이 정해졌다.
선언 당시 썼던 *의 역할은 그 순간 마무리 된 것이다.
이제 *없이 agePtr만 있어도 우리(컴터와 나)는 얘가 포인터 변수라는 걸 안다.
그런데 그런 상황에서 포인터 변수 앞에 *이 오면,
이 *은 아래와 같은 명령을 하고있는 것이다.
"포인터가 담고있는 메모리 주소로 찾아가서 그곳에 저장된 데이터 값을 갖고와"
이 3번 항목에서의 포인트는,
선언 당시의 '얘는 포인터 변수야'라고 말하는 <*>과
'그 주소로 찾아가 값을 가져와'라고 말하는 <*>을 완전히 구분해서 이해하는 것이다.
포인터 이해하기의 여정.
이게 끝이다.
이 핵심을 이해하지 못한 상태에서
배열, 인덱스, 이중포인터, 어쩌구 저쩌구 자꾸 개념이 늘어나니
모든게 꼬이면서 어려워져버렸다.
새로운 개념이 나와도 복잡할 것은 없다.
앞서 정리한 모든 원칙이 그대로 적용된다.
포인터의 활용과 관련한 내용은
앞으로 다른 글을 통해 정리해보겠다.
끗.
'CS Fundamentals > C' 카테고리의 다른 글
[C programming] main 함수를 return 0으로 끝내는 이유 (0) | 2023.12.13 |
---|