Developer

15.(C언어) 포인터 본문

Programming Language/C

15.(C언어) 포인터

DPhater 2020. 8. 1. 21:25

변수를 선언하면 컴퓨터의 메모리에 변수의 공간이 생성된다.

int a=100; 을 수행했을때 동작을 보자

그림과 같이 일정 공간 변수를 위해 공간을 확보하고 값을 저장하거나 사용한다.

그럼 변수a의 주소는 무엇일까? 이미 scanf를 배울때 변수의 주소를 얻는 방법을 배웠다. 바로 &(주소 연산자)이다.

#include<stdio.h>
int main(){
	int a=100;
	printf("%p\n",&a);
	return 0;
}

코드1 실행 결과

포인터용 서식 지정자 %p를 사용해 출력해보았다. 위의 실행 결과에 나오는 a의 주소이다. 이 주소는 컴퓨터마다, 실행마다 매번 다르게 정해진다. 그리고 출력된 포인터를 보면 16자리 인것을 알 수 있다. 지금 컴퓨터가 64비트 컴퓨터 이기때문이다. 32비트라면 8자리로 나올것이다.

주소를 구했다면 이 주소를 다른곳에 저장을 할 수 있어야 사용하기 좋을것이다.  바로 이 주소를 저장하는 변수가 포인터(pointer) 변수이다. 포인터 변수 선언은 *(Asterisk)를 사용한다.

자료형 *포인터이름

-->자료형 공간을 가리키는 포인터 라는 뜻

이제 포인터에 주소를 저장할 수 있다.

#include<stdio.h>
int main(){
	int a=10;
	int *ptr;  //포인터변수 ptr 선언
	ptr=&a;   // 변수 a의 주소를 포인터 ptr에 저장 
	printf("%p\n",&a);
	printf("%p\n",ptr);
	return 0;
}

코드2 실행 결과

ptr이라는 포인터에 a의 주소가 저장되어 있는걸 알 수 있다.

즉 ptr이 위의 그림과 같이 a가 있는 공간을 가리키는 것이다.

포인터 역참조

포인터에서 가리키고 있는 주소에 저장되어 있는 "값"을 사용 하기 위해서는 또 다시 *을 사용한다. 이것은 역참조 연산자라고 불린다.

역참조 연산자를 포인터앞에 붙혀주면 해당 포인터가 가리키는 공간으로 가서 저장되어 있는 값을 가져온다. 또한 역참조를 통해 포인터로 가리키는 변수에 값을 저장할 수 있다.

포인터를 선언할때도 *, 역참조를 할때도 *을 사용하는데 선언할 때는 이것이 "포인터다" 라고 알려주는 역할을 하는것이고, 이외에는 "포인터의 메모리를 역참조 하겠다"라는 뜻이다.

#include<stdio.h>
int main(){
	int a=10;
	int *ptr;  //포인터변수 ptr 선언
	ptr=&a;     //ptr이 a의 주소를 가리키게 만듬
	printf("%d\n",*ptr);   //ptr을 역참조 (가리키는 주소의 값을 가져옴)
	*ptr=25;     //역참조를 통한 저장(ptr이 가리키는 주소 즉 a에 25를 저장)
	printf("%d  %d",a,*ptr);
	return 0;
}

코드3 실행 결과

코드의 동작 절차를 그림으로 확인해 보자!

변수 a와 포인터 ptr을 선언 했을때의 모습이다.

ptr=&a를 통해 포인터 ptr이 a의 주소를 가리키게 하였다.

printf함수에서 포인터를 역참조하였다. ptr이 현재 a의 주소를 가리키고 있으므로

*ptr은 a가 가지고 있는 값이다. 따라서 10이 출력된다.

*ptr=25; 를 통해 값을 할당 하였다. *ptr은 위에서 말했듯이 현재 포인터가 가리키고 있는 주소공간에 저장된 값이다. 이 값을 25로 변경한다. 당연히 a의 내용은 25로 바뀐다.

포인터의 크기

32비트와 64비트는 포인터의 크기가 다르다.

주소공간의 크기가 다르기 때문이다. 32비트의 경우 4바이트이고, 64비트의 경우 8바이트의 크기를 가진다.

다시말해 64bit컴퓨터에서는 char*, long long*, int * 의 크기가 모두 8로 같다.

그렇다면 왜 자료형별로 포인터를 따로 만들까?

바로 역참조를 하기 위해서 이다. 자료형에 따라 역참조할때 메모리에 접근하는 방법이 달라진다.

위의 그림처럼 long long * (long long 자료형의 주소를 가리키는 포인터)의 경우 값을 가져올 때 long long의 크기인 8byte를 가져오는 것이다.

이중 포인터(포인터의 포인터)

포인터는 선언할 때 *를 사용하여 선언한다. 이중 포인터는 *을 두개 사용해 선언한다. 앞에서 말한 포인터도 결국 변수이기 때문에 메모리 공간에 저장되어있고, 이 포인터 역시 주소가 존재한다. 이중 포인터는 이러한 포인터의 주소를 저장하는 포인터이다.

#include<stdio.h>
int main(){
	int a=3;
	int *ptr;
	int **pptr;
	ptr=&a;
	pptr=&ptr;
	
	printf("%d  %d   %d\n",&a,&ptr,&pptr);
	printf("%d  %d   %d\n",a,ptr,pptr);
	printf("%d  %d   %d\n",a,*ptr,*pptr);
	printf("%d  %d   %d\n",a,*ptr,**pptr);
	return 0;
}

코드4 실행 결과

비교를 편하게 하기위해 모두 10진수 정수 형태로 출력하였다.

그림을 통해 코드의 진행을 차례대로 살펴보자

각 변수들이 선언 되었을때 모습이다. a는 3으로 초기화 되어 있으니 3이라는 값을 가지고있다. 각 변수들 아래에 적힌 숫자는 각 변수들의 주소이다. 이제 포인터 변수들에게 주소를 할당한 코드를 실행해보자!!

포인터 변수에 값이 생겼다. ptr 내부에는 a의 주소가 들어가 있어 ptr이 a를 가리키게 되었고, pptr(이중 포인터)에는 ptr(포인터)의 주소인 6487568이 들어가 있어 pptr이 ptr을 가리키게 되었다.

지금 위의 그림들로 2개의 printf출력 결과가 왜 저렇게 나왔는지 이해할 수 있다.

처음 printf는 각 변수들의 주소를 출력한다. (&가 주소 연산자 이므로)

2번 printf는 각 변수에 저장된 값을 출력하고있다.

바로 위의 그림에서 보이는것 처럼 a에는 3이라는 데이터가

ptr에는 a의 주소인 6487580이

pptr에는 ptr의 주소인 6487568이 들어있고 이 값들이 출력되는것을 확인할 수 있다.

이제 역참조를 해보자

*ptr은 ptr이 가리키고 있는 곳의 값 이므로 a가 출력되는것을 위에서 알게 됐다.

*pptr도 똑같이 동작한다고 생각하면 된다.

*pptr은 현재 pptr이 가리키고 있는곳의 값!! 즉 ptr에 저장된 값이다.

ptr에 저장된 값은 a의 주소이므로 *pptr은 a의 주소를 출력한다.

마지막 printf을 보면 **pptr으로 역참조를 두번하는데 순서대로 생각하면 된다.

pptr이 가리키고 있는 곳에서 가리키고 있는곳의 값이다.

차례대로 생각해보면 pptr이 가리키고 있는 곳은 ptr이다.

그리고 ptr이 가리키고 있는곳은 a이고, a는 3이라는 값을 가지고있다.

그러므로 **pptr은 3이다.

'Programming Language > C' 카테고리의 다른 글

17(C언어) 동적할당  (0) 2020.08.01
16.(C언어) 배열  (0) 2020.08.01
14.(C언어) 반복문_for,while  (0) 2020.08.01
13.(C언어) 조건문_if,switch  (0) 2020.08.01
12.(C언어) 논리 연산자,비트 연산자  (0) 2020.08.01
Comments