본문 바로가기
Language&Framework&Etc/C

포인터의 이해(12-2) 포인터와 관련 있는 연산자: &, *

by 머리올리자 2020. 12. 7.

&, * : 포인터 연산자

변수의 주소 값을 반환하는 & 연산자

& : 피연산자의 주소 값을 반환하는 연산자

#include <stdio.h>

int main(void)
{
	int num = 7;
	int* pnum = &num; // num의 주소 값을 반환해서 포인터 변수 pnum을 초기화

}

& 연산자의 피연산자는 변수(상수 X)

 

변수의 자료형에 맞지 않는 포인터 변수의 선언은 문제가 될 수 있다.

#include <stdio.h>

int main(void)
{
	int num1 = 7;
	double* pnum1 = &num1; // 일치하지 않음

	double num2 = 5;
	int* pnum2 = &num2; // 일치하지 않음
}

컴파일 에러는 발생하지 않지만 포인터 관련 * 연산 시 문제가 발생한다.

 

포인터가 가리키는 메모리를 참조하는 * 연산자

* 연산자는 포인터가 가리키는 메모리 공간에 접근할 때 사용하는 연산자

 

#include <stdio.h>

int main(void)
{
	int num1 = 7;
	int* pnum1 = &num1;     // 포인터 변수 pnum1이 변수 num을 가리키게 하는 문장
	printf("%d\n", *pnum1); 
	*pnum1 = 20;            // pnum1이 가리키는 변수에 20을 저장하라
	printf("%d\n", *pnum1); // pnum1이 가리키는 변수를 부호 있는 정수로 출력
}

포인터 변수 pnum1은 변수 num을 가리키고 있다.


따라서 *pnum1이 의미하는 바는 다음과 같다.

 

"포인터 변수 pnum1이 가리키는 메모리 공간인 변수 num에 접근을 해서..."

 

다음

*pnum1 = 20;              => "포인터 변수 pnum1이 가리키는 메모리 공간인 변수 num에 정수 20을 저장"

*printf("%d\n", *pnum); => "포인터 변수 pnum1이 가리키는 메모리 공간인 변수 num에 저장된 값을 출력"

 

예제

#include <stdio.h>

int main(void)
{
	int num1 = 13;
	int num2 = 150;

	int* pnum;

	pnum = &num1;
	(*pnum) += 100; // num1+=30과 동일

	pnum = &num2;
	(*pnum) -= 200; // num2-=30과 동일
	printf("num1 : %d, num2 : %d\n", num1, num2);

	return 0;
}

포인터 변수 pnum이 가리키는 대상이 num1에서 num2로 한차례 변경

 

다양한 '포인터 형'이 존재하는 이유

  • 포인터의 형은 메모리 공간을 참조하는 기준이 됨(int형 포인터 -> 4바이트, double형 포인터 -> 8바이트)
  • 포인터 형을 정의한 이유 : * 연산자를 통한 메모리 공간의 접근 기준을 마련하기 위함

아래 예시로 무엇이 문제인지 살펴봄

#include <stdio.h>

int main(void)
{
	double num = 3.14;
	int* pnum = &num;		// 형의 불일치
	printf("%d\n", *pnum);  // 예측 불가능한 의미 없는 출력

	return 0;
}
  • 경고 메세지는 출력되지만, 컴파일 에러가 발생하지 않는다.
  • C언어가 메모리 접근에 대한 유연성을 최대한 보장하기 때문에 -> 메모리 접근에 신중을 기해야 함
  • int형 포인터 변수 pnum은 double형 변수 num을 가리키게 됨
  • 메모리 공간에 저장된 값을 얻기 위해서 *하면 -> "4바이트로 읽어 들여서 이를 정수로 해석"
  • 의미 없는 일임

정리

포인터의 형이 존재하는 이유는 포인터 기반의 메모리 접근기준을 마련하기 위함.

포인터의 형이 존재하지 않는다면 * 연산을 통한 메모리 접근을 불가능하다.

 

잘못된 포인터의 사용과 널 포인터

포인터 변수 : 메모리 주소 값 저장

포인터 변수로 메모리 공간에 접근도 가능하기 때문에 포인터와 관련해서는 상당히 주의를 해야 함

 

1.

#include <stdio.h>

int main(void)
{
	int* ptr;
	*ptr = 200;
}

포인터 변수를 선언만 하고 초기화하지 않으면, 포인터 변수는 쓰레기 값으로 초기화 → 어디를 가르킬지 모름

이러한 상태에서 * 연산을 통해 값을 저장하는 것은 치명적인 결과로 이어짐

만약 ptr이 가리키는 메모리 공간이 매우 중요한 위치면, 시스템 전체에 심각한 문제를 일으킬 수도 있다.

 

2020-12-07 이와 관련해서 visual studio 2019에서 확인 결과 에러를 통해 문제를 방지해줌

2.

#include <stdio.h>

int main(void)
{
	int* ptr = 30;
	*ptr = 200;

}

30번지가 어딘 줄 알고 포인터 변수로 초기화 하는가? → 쓰레기 값으로 포인터 변수를 초기화한 것과 다르지 않다.

 

"그렇다면 포인터 변수는 어떤 값으로 초기화?"

 

포인터 변수를 우선 선언만 해놓고, 이후에 유효한 주소 값을 채워 넣을 생각이면 다음과 같이 초기화

 

#include <stdio.h>

int main(void)
{
	int* ptr1 = 0;
	int* ptr2 = NULL; // NULL은 사실상 0을 의미함

}

ptr1을 초기화하는 값 0을 가리켜 '널 포인터'

0번지를 의미하는 것이 아님.

 

"아무데도 가리키지 않는다!"는 것을 의미

 

참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.