포인터
변수의 값과 주소
변수를 선언하는 것은 메모리에 기억공간을 할당하는 것이며, 할당된 이후에는 변수명으로 그 기억공간을 사용한다.
변수가 선언되면 컴파일러는 심볼테이블(symbol table)이라는 변수의 목록을 만든다.
심볼테이블에는 변수이름과 자료형과 이 변수가 할당받은 메모리의 주소가 기록된다.
변수가 할당받은 공간에 변수의 값이 들어간다. 변수의 선언시에는 미지정값이 들어 있다.
변수 x와 y앞에 붙은 &는 주소 연산자이다. &x는 변수가 할당받은 메모리의 주소를 말한다.
메모리에는 바이트(byte)단위로 그 위치를 식별할 수 있는 물리적인 주소값이 있으며, 형식 지정자 %p는 주소를 16진수로 출력할 때 사용된다.
여기서 변수의 주소값 0x2003이나 0x2007은 정해진 값이 아니라 프로그램 실행시 할당되는 메모리 주소이기 때문에 매 실행마다 값이 달라진다.
포인터 변수
포인터 변수 또는 참조(reference)변수는 메모리의 주소를 값으로 가지는 변수로 프로그램에서 메모리에 직접 접근할 때 사용된다. 변수를 선언하면 그 자료형의 크기만큼 메모리에 연속된 바이트의 기억공간이 할당되는데 그 첫번째 바이트의 주소값이 포인터이다.
일반 변수의 포인터(여기서는 주소를 뜻한다)를 구하기 위해서는 주소연산자(& : 엔퍼샌드)를 사용한다.
포인터 변수는 항상 메모리의 주소를 가지고 있는데, 이것을 포인터 변수가 그 주소의 메모리를 가리킨다고 한다. 포인터 변수는 그 타입이 관계없이 크기는 4바이트이다. 이는 컴퓨터 주소체계가 4바이트로 표현된다는 것을 의미한다.
포인터 변수를 선언할 때는 * 연산자를 사용하며, 포인터 변수가 가리키는 대상 자료형을 선언해 주어야 한다.
예를들어, A라는 창고에 A, B, C 선반이있는데 "A창고에서 뭐좀 가져와라"라고하면 A창고에서 A,B,C 어느 선반의 물건을 가져와야할지 모른다. 이렇듯, 포인터를 만들때는 주소를 저장하는데, 주소가 무슨 타입(어느 선반인지)인지 명시해줘야한다.
포인터를 저장한 포인터 변수도 참조연산자로 그것이 가리키는 기억공간 또는 그 기억공간의 값을 사용할 수 있다.
지금까지 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개만 썼는데, 다음에는 **를 썼을때를 알아보도록 하자.
'개발자과정준비 > C' 카테고리의 다른 글
[C] 포인터 - 3 (포인터 초기화, 포인터 연산) (0) | 2020.10.20 |
---|---|
[C] 포인터 - 2 (0) | 2020.10.15 |
[C] 반복문을 활용해서 구구단 출력 (0) | 2020.10.13 |
[C] Scanf 런타임오류 해결 방법 (0) | 2020.10.10 |
[C] 반복문(while, do-while, for) (0) | 2020.10.08 |