C언어에서 포인터를 잘 이해하기 위해서는 메모리 공간에 대한 그림을 잘 그리는 것이 중요하다.
또한, C언어에서는 시작번지만을 가지고 위치를 표현한다.
자료형 int num = 7; 의 위치가 어디냐?
라는 질문을 했을 때, 4바이트이므로 어디부터 어디까지입니다. 라는 대답을 할 수 있다.
하지만, 시작번지를 알면 int형이 4바이트임을 알고 있으므로 끝은 쉽게 계산할 수 있기 때문이다.
이 시작번지가 바로 주소값이며 이 역시 정수이다. 따라서, 이것 역시 저장 가능하며 이를 저장하기 위한 변수가
바로 포인터 변수이다.
즉, 포인터 변수는 어떤 변수에 대한 메모리에 할당된 주소값을 저장하기 위한 변수이다.
int num = 7; // 일반적인 변수 선언
int *ptr = # // 포인터 변수 선언 후 num의 주소값을 포인터 변수 ptr에 저장
ptr는 포인터 변수명이며, int *는 int형 변수의 주소값을 저장하겠다는 의미의 포인터 변수의 선언이다.
&연산자 : 주소값을 가져오는 연산자이므로. 즉 ptr = &num의 경우 num의 주소값을 ptr변수에 저장한다.
그리고 이러한 것을 포인터 변수 ptr이 int형 변수 num을 가리킨다. 라고 표현한다.
말그대로, 포인터 변수 ptr에는 변수 num의 주소값이 들어있고, 아래 그림과 같이 가리키고 있다.
-
[포인터 변수 선언하기~!]
포인터 변수는 가리키고자 하는 변수의 자료형에 따라서 선언하는 방법이 달라진다.
-
int형 변수를 가리키는 포인터 변수의 자료형은 int
-
double형 변수를 가리키는 포인터 변수의 자료형은 double
-
char형 변수를 가리키는 포인터 변수의 자료형은 char
아래와 같은 변수의 자료형에 맞지 않는 포인터 변수의 선언은 문제가 발생할 수 있다.
int main(void) {
double number = 10.123;
int * ptr = &number;
printf("%d", *ptr);
}
컴파일 에러는 발생하지 않지만, 많은 문제를 일으킬 수 있다.
다양한 포인터 형이 존재하는 이유는 메모리 공간을 참조하는 기준이 된다.
즉, 포인터 형의 정의는 *연산자를 통핸 메모리 공간의 접근 기준을 마련하기 위해서이다.
위에 같은 코드에서는 변수는 double형 8바이트이지만, int *이므로 ptr 포인터변수는
intg형이므로 4바이트의 값을 구하게 되고 결국은 쓰레기값이 생성되므로 무의미하다.
[포인터와 관련이 있는 연산자들~!]
&연산자 : 변수의 주소값을 반환하는 연산자.
- 피연산자의 주소값을 반환하는 연산자이다.
- &num 과 같이 우측에 변수가 와야하고, 상수가 올 수 없다.
*연산자 : 포인터가 가리키는 메모리를 참조하는 연산자.
- 포인터가 가리키는 메모리 공간에 접근할 때 사용하는 연산자이다.
- 여태껏 포인터 변수에 주소값을 저장만 했고, 사용하지 않았다. 사용하지 않을거면 왜 포인터변수를 만드나?
바로 이 연산자를 이용하여 포인터 변수가 가리키는 메모리 공간인 변수에 접근하여 값을 조작할 수 있다.
다음과 같이 사용이 가능하다.
int main(void) {
int num = 20;
int * pnum = #
*pnum += 50; // pnum = pnum + 50
printf("%d", *pnum);
}
결과는 : 70
[잘못된 포인터의 사용과 널 포인터의 사용]
포인터 변수에는 메모리 주소값이 저장되고, 이를 이용하여 해당 메모리 공간에 접근도 가능하기 때문에
상당히 주의를 기울여야 한다.
1. 포인터 주소값을 넣지 않을 때.
int * ptr; // 주소값을 저장하지 않았다!!!!
ptr = 500; // 그리고 500을 대입하였다!!!!
이럴 경우. 쓰레기값이 삽입된다. 그런데 그 쓰레기값이 하필 OS에서 중요한 메모리 공간이었다면?
치명적인 문제가 발생할 수도 있다. 요즘은 컴퓨터가 많이 좋아져서 자동적으로 프로그램을 중지시켜
메모리에 접근을 방지한다고 한다.
하지만, 이렇게 쓰게 되면 쓰나마나.
2. 변수의 주소값을 이상하게 넣었을때.
int * ptr = 255; // 주소값이 255가 어디여???
ptr = 500; // 그리고 500을 대입하였다!!!!
255가 어딘줄 알고 저장을 해? 이것 역시 쓰레기값이다.
3. 포인터 변수 초기화 시키는 방법 -> NULL
포인터 변수를 먼저 선언해놓은 뒤, 나중에 사용하고자 한다면 0 또는 NULL로 초기화를 해준다.
int *ptr1 = 0;
int *ptr2= NULL;
0을 널 포인터라고 하며, 0은 0번지가 아니라 아무것도 가리키지 않는다는 의미이다.
NULL 역시 이는 실제로 상수 0으로 정의되어 있다.