본문으로 바로가기

[C] 포인터 - 1

category 개발자과정준비/C 2020. 10. 14. 13:56
반응형

포인터

변수의 값과 주소

변수를 선언하는 것은 메모리에 기억공간을 할당하는 것이며, 할당된 이후에는 변수명으로 그 기억공간을 사용한다.

 

 

변수가 선언되면 컴파일러는 심볼테이블(symbol table)이라는 변수의 목록을 만든다.

심볼테이블에는 변수이름과 자료형과 이 변수가 할당받은 메모리의 주소가 기록된다.

변수가 할당받은 공간에 변수의 값이 들어간다. 변수의 선언시에는 미지정값이 들어 있다.

변수 x와 y앞에 붙은 &는 주소 연산자이다. &x는 변수가 할당받은 메모리의 주소를 말한다.

메모리에는 바이트(byte)단위로 그 위치를 식별할 수 있는 물리적인 주소값이 있으며, 형식 지정자 %p는 주소를 16진수로 출력할 때 사용된다.

 

16진수에서 10, 11이 나왔는데 오타이니까 넘어가자

 

여기서 변수의 주소값 0x2003이나 0x2007은 정해진 값이 아니라 프로그램 실행시 할당되는 메모리 주소이기 때문에 매 실행마다 값이 달라진다.

 

포인터 변수

포인터 변수 또는 참조(reference)변수메모리의 주소를 값으로 가지는 변수로 프로그램에서 메모리에 직접 접근할 때 사용된다. 변수를 선언하면 그 자료형의 크기만큼 메모리에 연속된 바이트의 기억공간이 할당되는데 그 첫번째 바이트의 주소값이 포인터이다. 

 

 

일반 변수의 포인터(여기서는 주소를 뜻한다)를 구하기 위해서는 주소연산자(& : 엔퍼샌드)를 사용한다.

 

 

포인터 변수는 항상 메모리의 주소를 가지고 있는데, 이것을 포인터 변수가 그 주소의 메모리를 가리킨다고 한다. 포인터 변수는 그 타입이 관계없이 크기는 4바이트이다. 이는 컴퓨터 주소체계가 4바이트로 표현된다는 것을 의미한다.

 

포인터 변수를 선언할 때는 * 연산자를 사용하며, 포인터 변수가 가리키는 대상 자료형을 선언해 주어야 한다.

예를들어, A라는 창고에 A, B, C 선반이있는데 "A창고에서 뭐좀 가져와라"라고하면 A창고에서 A,B,C 어느 선반의 물건을 가져와야할지 모른다. 이렇듯, 포인터를 만들때는 주소를 저장하는데, 주소가 무슨 타입(어느 선반인지)인지 명시해줘야한다.

 

int형 포인터 변수 선언

 

포인터를 저장한 포인터 변수도 참조연산자로 그것이 가리키는 기억공간 또는 그 기억공간의 값을 사용할 수 있다.

 

 

 

지금까지 16진수를 출력할때 %x를 사용했는데, 원래는 %08x를 사용해야한다.

#include <stdio.h>

int main() {

	printf("%d\n", 3451853);
	printf("%10d\n", 3451853);      // 10칸 확보해서 숫자출력(칸남으면 빈칸)
	printf("%011d\n\n", 3451853);   // 11칸 확보해서 숫자출력(빈칸에는 0 채움)
	// C#은 string.format("%d", 100); 으로 printf처럼 사용할 수 있다.
	
	printf("%o\n", 3451853);
	printf("%x\n", 3451853); 
	printf("%X\n", 3451853);        // 16진수에서 알파벳을 대문자로 출력
         printf("%08X\n", 3451853); // 8칸을 확보하고 16진수로 출력(빈칸은 0 채움)
	printf("%p\n", 3451853);        // %08X와 똑같이 출력된다 
	printf("0x%08X\n", 3451853);    // 16진수의 가장 우아한 형태
	printf("0x%p\n", 3451853);      // 16진수의 가장 우아한 형태2
}

 

 

아래의 코드를 디버그 모드로 실행해보자.

#include <stdio.h>
int main() 
{
	int		iNum;
	int *   p;

	iNum = 100;
	p = &iNum;
	
	printf("iNum의 값   : %d\n", iNum);
	printf("iNum의 주소 : 0x%p\n", &iNum);

	printf("p의 값      : 0x%p\n", p);  // p의 주소는 어디인가?
	printf("p의 주소    : 0x%p\n", &p); // p는 어떤 값을 가지고있는가?
	printf("*p의 값     : %d\n\n", *p); // p가 가지고 있는 값으로 가보자.

	printf("/////////////////////////////////////////////////////\n\n");

	printf("iNum의 값   : %d\n", iNum);
	printf("iNum의 주소 : 0x%p\n", &iNum);
	printf("*&iNum      : %d\n\n", *&iNum);  // &iNum의 값을 따라가보자. -> iNum값(*&가 만나면 상쇄된다!!!)

	printf("p의 값      : 0x%p\n", p);
	printf("p의 주소    : 0x%p\n", &p);
	printf("*&p         : %d\n", *&p);  // &p의 값을 따라가보자 -> iNum의 주소가 출력
	printf("*p의 값     : %d\n\n", *p);

	// 별의 개수만큼 변수 앞에 *를 붙일 수 있다!!

	printf("/////////////////////////////////////////////////////\n\n");

	printf("iNum        : %d\n", iNum);
	printf("*&iNum      : %d\n", *&iNum);
	printf("p           : %p\n", p);
	printf("*&p         : %p\n", *&p);

	return 0;
}

 

 printf에 *p를 해주면 p의 값인 주소로 찾아가봐라. 라는 뜻이다. 그래서 p의 값인 주소(iNum)에 값은 iNum의 값이므로 100이 출력되는 것이다. 

 또, printf에 *iNum을 선언하면 오류가 뜬다. 컴파일러는 *iNum의 *을 곱셈으로 인식해서 왜 왼쪽에는 아무 숫자도 안썼니?라고 오류가 뜬다.(옛날 비쥬얼 스튜디오 버전에는 그랬다고한다... 지금은 포인터 변수를 선언하지 않았다고 빨간줄이 뜬다)

 

변수 이름 자료형 메모리 주소
iNum int 0x19FED8
p int* 0x19FECC

<심볼테이블>

 

 

그림으로 대략적으로 알아본 변수값, 변수 주소

 

디버그 모드로 알아본 실제 변수값, 변수 주소 값

 

지금까지 *은 3가지의 용도로 쓰인다.

1. 곱셈연산을 할때

2. 포인터를 만들때

3. (위의 예제에서는)printf에서 주소를 따라가라고 할때 

 

 

오늘은 포인터 변수를 선언할때 * 을 1개만 썼는데, 다음에는 **를 썼을때를 알아보도록 하자.

반응형