Taking baby-developer steps
9. 포인터 - 포인터의 개념 / 포인터 관련 연산자(주소연산자, 포인터, 간접 참조 연산자) / 포인터의 기능 본문
9. 포인터 - 포인터의 개념 / 포인터 관련 연산자(주소연산자, 포인터, 간접 참조 연산자) / 포인터의 기능
Surin Lee 2021. 3. 24. 23:52포인터
향후 고급 프로그래머가 되기 위해선, C언어의 포인터를 이해하고, 더 하위 레벨의 언어라고 할 수 있는 어셈블리어 또한 접하게 될것이다. 일반적인 웹 및 앱 개발을 할 땐 몰라도 문제는 없으나, 코어 모듈 개발을 할 필요가 있다면 반드시 잘 알고 넘어가야 할 부분이다.
포인터의 개념
지금까지 변수는 그 자체로 자신의 자료형에 맞는 값을 저장했다. (ex> int, double) 포인터(Pointer) 변수는 특이한 변수로, 메모리 주소를 저장한다.(메모리에 주소 값 자체를 담는다.) 또, 단순히 주소 값만 저장하는게 아니라, 어떤 자료형의 주소 값인지를 함께 저장한다.
어떤 int형 변수를 만들었다고 하면, 이 int형 변수는 컴퓨터 메모리 공간 어딘가에 저장이 된다. 이 때, 그러한 변수의 위치 값을 가리키고 있는 int형 포인터가 존재할 수 있다.(*로 표시한다.)
- 포인터는 특정한 변수 자체가 존재하는 메모리 주소의 값을 가진다. 즉, 컴퓨터 메모리에 바로 접근 할 수 있게 한다.
기존에는 변수 a를 이용해 5라는 값을 담아서 처리를 했었는데, 포인터b(*b)가 a의 주소, 즉 이 a 변수를 가리키도록 만들어서도 "5"라는 값에 접근 할 수가 있다.
또, 이 *b도 "변수"이기 때문에, 특정한 주소 값을 가진다. 즉, a와 b 모두 각기 다른 주소 값을 가진 변수 라고 할 수 있다. 다만 b는 포인터 변수이기 때문에, a라는 변수의 '주소'를 그 값으로 가지고 있다.
int *b = &a ;
위에서 처럼, '선언할 때' 쓰는 '*'는 포인터 변수임을 알려주기 위한 목적을 가진다.
이후에 *b라고 쓰게 되면, 이것은 포인터 변수 b가 가리키는 주소의 값(위의 경우 a의 값인 '5')을 의미한다. 이런 맥락에서 이 '*'을 '간접 참조 연산자'라고 부른다.
포인터 관련 연산자
주소 연산자(&)
변수 앞에 붙어서 변수의 메모리 시작 주소 값(흔히, 포인터 변수의 '값'으로 들어갈 수 있는 요소)을 구한다.
ex) scanf()에 &a와 같은 형식으로 넣어주는 이유 : 특정한 변수가 메모리 내에 존재하는 주소 값 자체를 확인(주소를 가져온다)
포인터(*)
포인터 변수를 선언할 때 사용한다.
간접 참조 연산자(*)
선언된 포인터 변수가 가리키는 변수(값)를 구한다. (선언 이후에 사용하게 되면 포인터 변수가 '가리키는 변수의 값'을 가지게 된다.)
따라서, 선언 할 때와 선언 이후의 포인터 연산자(*)는 생긴 것만 같지, 기능은 다르다.
포인터의 개념
실제로 int a = 5;와 같이 변수를 할당하면, 메모리 주소상에서는 다음과 같이 기록된다. int형은 4B를 차지하므로, 메모리 주소를 1B 씩 표현 할 때 4칸을 차지한다.
포인터의 강력한 기능
- 포인터는 컴퓨터 시스템의 특정한 메모리에 바로 접근 할 수 있다.
따라서 기존에 존재하던 중요한 메모리 영역에 접근하지 않도록 해야 한다. 따라서 다음과 같은 코드는 굉장히 위험한 코드이다.
int *a = 0x33484735;
*a = 0;
'0x33484735'라는 이 주소가 정확히 어떤 역할을 하는 건지, 확실하게 이해할 수 없기 때문에, 자신이 컨트롤 할 수 있는 범위가 아닌 이상 특정한 주소 값에 함부로 접근해서 다른 값으로 덮어 쓰면 안된다.
- 포인터는 다중으로 이용할 수 있다.
즉, 포인터의 포인터의 포인터의 포인터를 만드는 것도 가능하다. 이와 같은 방법은 후에 게임 등을 개발 할 때, 난독화 기법으로도 사용 할 수 있다.
#include <stdio.h>
int main(void){
int a =5;
int *b = &a;
int **c = &b;
printf("%d\n", **c);
return 0;
}
- 배열과 포인터는 사실 동일하다
배열과 포인터는 서로 상호 치환되어서 사용될 수 있다. 배열을 선언한 이후에는 그 이름 자체가 포인터 변수와 동일하다.(그 이름 자체를 포인터 변수처럼 쓸 수 있다.)
#include <stdio.h>
int main(void){
int a[] = { 1, 2, 3, 4, 5, 6};
int *b = a;
printf("%d\n", b[2]);
return 0 ;
}
이때, 포인터 b를 선언할 때 a 앞에 주소 연산자가 없는 것을 볼 수 있다. 배열의 이름 자체를 주소 값으로 사용하고 있기 때문인데, 내부적으로 '배열의 이름 자체'는 '주소 값 자체'를 가지고 있기 때문에, 주소 연산자를 안 붙이고 사용할 수 있는 것이다. 후에 공부할 내용이지만, 배열의 이름 자체는 배열의 첫번째 원소의 주소 값과 같다(a = &a[0])
'CS 지식 > C언어_basic' 카테고리의 다른 글
11. 문자열 - 문자열의 개념 / 널(NULL)값 / 문자열과 포인터 / 문자열 리터럴 / 문자열 입출력 함수 / gets () 함수 /gets_s() 함수 / sizeof()함수 / 문자열 처리 함수 (0) | 2021.03.25 |
---|---|
10. 문자 - 아스키 코드 / 문자 입출력 함수 / 문자와 버퍼 (0) | 2021.03.25 |
8. 배열 - 배열의 선언과 초기화 / INT_MIN / 문자열과 배열 (0) | 2021.03.19 |
7. 함수 - 함수/재귀함수 (0) | 2021.03.19 |
6. 반복문 - For문 / 무한루프 /While 문 / 중첩된 반복문/ for문과 while문의 관계 (0) | 2021.03.19 |