포인터는 역참조가 목적이다.
포인터 역참조는, 변수를 사용하는 것과 목적이 같다.

 

 

 

 

 

1. 변수와 메모리

 

더보기

1.1  빌드와 실행


- 빌드

 

- 실행

 

- 포인터의 사용 목적

 

프로그램이 실행될 때, 메모리(RAM, 주기억장치)에 로드된 값(Data, 11001010)을 메모리의 주소값으로 참조하고자 한다. 이는 변수와 값을 사용하는 방법과 동일한 목적이지만, 방법이 다를 뿐이다.

 

그렇기 때문에 포인터의 학습의 시작은,

1) "변수와 값"을 사용하는 방법, 그리고 2)"포인터와 값"을 사용하는 방법을 명확하게 구분하는 것으로 시작해야 한다.

 

 

 

 

1.2 용어 명확히하기

 

- 소스코드를 볼 때, 메모리 구조가 머리속에 그려졌으면 좋겠다.

 

프로그램은 1)데이터(Data)와 2)로직(Logic)으로 이루어진다.

 

변수는 데이터(Data) 를 저장하기 위한 메모리 공간의 이름으로, 이진수(11001010)를 대신하여 사용한다.

대명사와 사물의 이름을 붙여 편리하게 사용하는 이유와 같다.

 

자료형(Data Type)은 변수의 특징(크기 등)을 추가한다.

 

 

 

 

1.3 메모리 그리기

 

1.3.1 (예제1) int형 변수와 메모리

 

#include <stdio.h>

void main()
{
    int iNum = 90;

    printf("iNum 정수값: %2d\n", iNum); // num 변수에 담긴 "정수 값 90" 출력
    printf("iNum 크기  : %2ld Byte\n", sizeof(iNum));
}

/* 실행 결과
iNum 정수값: 90
iNum 크기  :  4 Byte */

 

 

 

1.3.1 (예제2) char형, long 변수와 메모리

 

#include <stdio.h>

void main()
{
    char cNum = 65;
    long lNum = 77;

    printf("cNum 정수값: %d, 크기 %ld Byte\n", cNum, sizeof cNum);
    printf("lNum 소수값: %ld, 크기 %ld Byte\n", lNum, sizeof lNum);
}

/* 실행 결과
cNum 정수값: 65, 크기 1 Byte
lNum 소수값: 77, 크기 8 Byte */

 

 

 

2. 변수의 메모리 주소 (Value) 가져오는 방법

 

더보기

 

2.1 포인터 관련 연산자: &

 

 &  Ampersand, 앰퍼센드  주소 연산자 변수명과 함께 사용한다.
변수가 참조하는 메모리 공간의 첫 Byte 주소
*  Asterisk, 애스터리스크
 참조 연산자 1) 포인터 변수 선언시 사용 
2) 포인터 변수를 역참조하여 데이터에 접근할 때 사용

 

&, * 기호는, ++ 연산자처럼, 변수를 수식하는 "주소 연산자"와, "참조 연산자"다.

 

우선 & (엠퍼센드) 연산자만 기억하고, 메모리 주소값을 가져오는 방법을 익힌다.

& (엠퍼센드) 연산자는, % 퍼센트 라는 이름과 유사해서 외우기 쉽다.


& 연산자는 프로그래밍에서 AND, 즉 "같다"는 의미다.

"&변수" 의미는, 변수와 같은 것, 즉 메모리 주소다.

  

 

 


2.2 변수가 참조하는 "메모리의 주소값" 가져오는 방법

 

변수명 앞에 &(엠퍼센드) 연산자를 붙이면, 변수가 참조하는 메모리 공간의 첫번째 바이트 주소를 참조한다.

printf( ) 함수를 사용하여 출력할 경우, %p %x 포멧을 사용하여 메모리 주소값을 출력 할 수 있다.

 

변수명 앞에 &(엠퍼센드) 연산자를 붙여, "주소값" 을 가져올 수 있다.

모든 선언된 변수명 앞에 &연사를 붙여 해당 변수가참조하는메모리 공간의 첫번째 바이트를 식별 할 수 있는 고유한 메모리 주소값을 참조하여 "" 으로 사용할 수 있다.      

 

#include <stdio.h>

void main()
{
	int iNum = 90;

	printf("iNum 정수값: %d\n",  iNum);  // num 변수에 담긴 "정수 값 90" 출력
	printf("iNum 주소값: %p\n", &iNum);  // num 변수가 참조하는 첫번째 Byte "메모리 주소 값" 출력
 }

 

 

 

2.3 추가 예제

 

#include <stdio.h>

void main()
{
    char cNum = 65;
    printf("cNum 정수값: %d, 크기 %ld Byte\n", cNum, sizeof cNum);
    printf("cNum 주소값: %p 크기, %ld Byte\n",&cNum, sizeof &cNum);
    
    long lNum = 77;
    printf("lNum 정수값: %ld, 크기 %ld Byte\n", lNum, sizeof lNum);
    printf("lNum 주소값: %p 크기, %ld Byte\n",&lNum, sizeof &lNum);
}

/* 실행 결과
cNum 정수값: 65, 크기 1 Byte
cNum 주소값: 0x0061FF17 크기, 8 Byte
lNum 정수값: 77, 크기 8 Byte
lNum 주소값: 0x0061FF1D 크기, 8 Byte */

 

  

포인터는 메모리의 주소값을 사용하는 방법이다.

다음 3번 내용에서, 포인터 변수를 선언하고 "주소값"을 포인터 변수에 할당해보자.

 

 

3. 포인터 변수(Variable)를 선언하고, 메모리 주소값 담기

 

더보기

 

3.1 포인터 관련 연산자: * 

 

Ampersand, 앰퍼센드  주소 연산자 변수명과 함께 사용한다.
변수가 참조하는 메모리 공간의 첫 Byte 주소
*  Asterisk, 애스터리스크
 참조 연산자 1) 포인터 변수 선언시 사용 
2) 포인터 변수를 역참조하여 데이터에 접근할 때 사용

 

*(에스터리스크, 별) 기호가 의미하는 연산자는, 1)포인터 변수를 선언할 때와 2)포인터 변수를 역참조 할 때 사용한다.

우선, *(에스터리스크, 별) 연산자를 사용해 포인터 변수를 선언하고, &변수명을 사용해 주소값을 할당하는 방법만 알아보자.

 

 

 

 

 

3.2 메모리 주소값을 "포인터 변수"에 저장하는 방법

 

#include <stdio.h>

int main(void)
{
	int iNum = 90;

	printf("iNum 변수의 정수값: %d\n",  iNum);  // num 변수에 담긴 "정수 값 90" 출력
	printf("iNum 변수의 주소값: %p\n", &iNum);  // num 변수가 참조하는 첫번째 Byte "메모리 주소" 출력

	int* p_iNum = &iNum;                  // 실제로 p_iNum 같은 형태로 사용하지 않는다. 하나의 예시다.

	printf("p_iNum 포인터 변수 값: %p\n",  p_iNum);  // num 변수에 담긴 "정수 값 90" 출력
	printf("p_iNum 포인터 변수 역참조 값: %d\n", *p_iNum);  // num 변수가 참조하는 첫번째 Byte "메모리 주소 값" 출력
    
    return 0;
 }

 

 

 

 

3.3 포인터 변수 선언시, * 연산자 위치

 

자료형 int 와 변수명 p_iNum 사이에 *(에스터리스크, 별) 연산자를 추가하여 포인터 변수를 선언한다. 

* 연산자의 위치는 자유롭지만, 이론산 변수명 앞에 전위 연산자로 붙여야 한다.

 

다음 4번 내용에서 * 의미에 대해 깊게 이해할 필요가 있다.

 

포인터 선언시 * 연산자를 선언하는 위치는 자유롭지만, 이론적으로 변수명 앞에 붙는다.

 

 

 

 

3.5 추가 예제

 

- 아래 소스코드를 구현하고, 메모리 구조가 머리에 그려지는지 확인한다.

 

 

 

4. 변수와 값, 그리고 변수 선언에서 자료형의 의미 

 

더보기

 

4.1 기본 자료형과 선언의 의미

 

4.1.1 파이썬을 사용할 때, 자료형을 선언하지 않았습니다.

 

 

4.1.2 하지만, 아래 그림처럼 C언어는 변수명 앞에 자료형을 선언해야 합니다.

"기본 자료형과 변수명을 선언한다."의 의미를 저장될 데이터 크기와 메모리 관점에서 함께 생각해봅시다.  

 

 

기본 자료형을 선언하는 목적은, 변수명으로 참조하는 메모리 공간의 특성(크기 등)을 명시하는 작업입니다.

선언과 초기화 이후, 개발자는 변수명만 사용하더라도, 선언시 사용한 자료형의 크기라는 특징과 할당한 값을 의미합니다. 

 

 

 


4.2 인간이 대명사를 사용하는 이유 - 속성과 특성

 

제 이름은 홍길동 입니다.

홍길동 이라는 이름이 없다면, 저를 어떻게 식별할 수 있을까요?

외향을 장황하게 설명하는 등, 굉장히 어려운 일이 될 것입니다.
인간이 사물에 이름을 붙이는 이유에 대해 고민해 봅시다. 

자동차에 "자동차"라는 이름을 붙일 수 없다면 어떻게 될까요?

 

변수를 사용하는 이유도 마찬가지입니다.

메모리 주소나, 데이터 값의 2진수를 나열하여 사용한다면 굉장이 어려운 일이 됩니다.

 

 

자료형을 선언하는 작업은 변수명에 크기라는 특성을 부여하는 작업입니다. 

int iNum; 과 같이 한 번 선언 한 뒤 iNum 변수명만 사용합니다. 이 때, iNum 변수의 크기를 4Byte 라는 특성을 고려하여 프로그래밍하게 됩니다. 

 

데이터(Data)는 변수명을 대신 사용하고 에 담기고, 그 크기가 달라지는 특징이 있습니다.
단적인 예로, 만약 int형 데이터 4TB 를 저장했는데, 나중에 파악해보니 char 형만으로 충분했다면, 1/4인 1TB 용량만으로 저장이 가능해집니다.
이러한 최적화를 위해 데이터의 크기를 한정하는 작업이 자료형과 변수명을 선언하는 작업입니다. 

 

 

 

 

 

4.3 포인터 변수 선언과 기본 자료형 변수 선언의 의미

 

 

기본 자료형과 포인터 자료형 모두 동일하게 변수명을 선언하고, 을 할당합니다.

차이점은 포인터 변수는 선언시, 변수명 앞에 "기본 자료형"과 " * 연산자" 2가지가 사용되었습니다.

 

기본 자료형 선언시 사용한 "자료형"은 변수의 크기라는 1가지 특성을 의미합니다.

포인터 변수 선언시에 "자료형"과 "*연산자"는 변수명에 2가지 특성이 부여되었음을 의미합니다.

 

다음 5번에서 이 2가지 특성에 대해 자세히 이해해야 합니다.

 

 

5. 포인터 변수의 특성 ,첫번째 - 크기

 

더보기

 

5.1  "메모리 주소 값" 데이터 크기는 일정하다.

 

기본 자료형과 값을 사용할 때, 값의 크기에 따라 기본 자료형을 다양하게 사용했다.

하지만, 포인터 변수에 담기는 메모리 주소값의 크기는 일정하다. 

즉, 1) 포인터 변수를 사용할 때 자료형을 다양하게 쓸 이유가 없으며,  2)포인터 변수를 선언할 때 사용하는 자료형은 포인터 변수의 크기를 의미하지 않는다.

 

2^32 = 4,294,967,296(약 43억) - 32bit CPU 에서 최대 메모리 크기가 4GB인 이유

2^64 = 18,446,744,073,709,551,616 (약 1844경)

 

 

32bit 운영체제에서  

  • 메모리의 주소(포인터)는 4byte (32bit = 8bit × 4byte) 크기를 가진다.
  • 포인터 변수는 4byte 크기를 가진다.

 

64bit 운영체제에서 

  • 메모리의 주소(포인터)는 8byte (64bit = 8bit × 8byte) 크기를 가진다.
  • 포인터 변수는 8byte 크기를 가진다. 

 

 

 

 

5.2  포인터 변수 선언과 사용(2) - 포인터 변수의 특성 , 첫번째 - 크기

 

포인터 변수는 메모리 주소값(0x0061FF17)을 담고 있다.

포인터 변수에 담기는 데이터(Data) 값의 크기는 일정합니다.

그렇기 때문에 기본 자료형 처럼, 크기를 의미하는 특징을 명시할 이유가 없습니다.

 

 

선언시 사용한 *연산자의 의미는, 변수가 포인터 변수라는 특징을 부여하는 역할입니다.

그리고 포인터 변수 선언시 사용된 기본 자료형은 변수의 크기를 의미하지 않습니다.

포인터 변수 선언시 사용된 기본 자료형은, 변수명이 참조하는 메모리 공간의 크기라는 특성을 의미하지 않습니다.

 

포인터 변수만이 가지는 특별한 기능인, "역참조"라는 기능에 사용하되는 특성입니다.

아래 6번에서 일반 변수와 다른, 역참조의 기능에 자세히 이해합니다.

포인터는 역참조를 위해 존재합니다. 

 

 

6. 포인터 변수의 특성, 두번째 - 역참조

 

더보기

 

6.2 포인터 변수 선언과 사용(1) - 변수와 값 관점

 

#include <stdio.h>

void main()
{
    int    iNum = 90;
    int *p_iNum = &iNum;

    printf("  iNum 변수의 정수 값: %d\n",   iNum);
    printf("p_iNum 포인터 변수 값: %p\n", p_iNum);
}


그리고 변수명을 사용하는 관점에서 기본 자료형 변수와 포인터 자료형 변수는 사용법이 완벽히 동일합니다.

 

변수의 선언과 초기화 이후, 변수를 사용하는 관점에서 생각하면

일반 변수(iNum)는 값(90)을 담고 있습니다.

포인터 변수(p_iNum)는 메모리 주소값(0x0061FF17)을 담고 있다.

포인터 변수의 사용은, * 연산자 없이, 일반 변수처럼 변수명(p_cNum)만 사용한다. 

대명사에 특성을 부여한 뒤, 부여할 때 사용한 특성 정보를 굳이 반복하여 사용하지 않습니다.

 

int iNum; 을 한 번 선언 한 뒤 iNum 변수명만 사용합니다.

int *p_iNum을 한 번 선언한 뒤, p_iNum 변수명만 사용합니다.

 

하지만, 일반 변수와 달리, 포인터 변수에는 "역참조" 라는 특별한 사용 방법(기능)이 추가되어 있습니다.

 

이 기능은 iNum++; -iNum 과 같이 변수명을 연산자로 수식하여 사용하는 방법 한가지가 추가된 것에 불과합니다.

int iNum = 90;

iNum++; 

int a  = -iNum;

 

 


6.2 포인터 변수 선언과 사용(2) - 포인터 변수의 특성 ,두번째 - 역참조 기능

 

- 역참조의 의미

 

예제1) char 포인터 변수의 역참조

 

포인터 변수를 선언할 때,  *기호를 포함하여 변수명과 함께 *p_cNum, *p_iNum 과 같이 사용하고,

포인터 변수의 사용은, * 연산자 없이, 기본 자료형 변수처럼 변수명(p_cNum)만 사용합니다.

 

선언시 사용하는 char, int, double 은 포인터 변수의 크기와 관련이 없습니다.

포인터 변수의 사용은, 변수명(p_cNum)만 사용합니다.

 

포인터 선언시 사용하는 char 자료형은, 포인터 변수의 역참조 및 포인터 연산에 사용된다.

char, int, double 자료형은 포인터 변수의 크기(주소 값 크기)와 관련 없습니다.

 

포인터 변수의 역참조 시, 얼마만큼의 공간(Byte)를 가져올 것인지에 대한 특성을 부여합니다.

 


예제2) int 포인터 변수의 역참조

 

 

 

 

6.3 예제


예제1) char 포인터 변수의 역참조

#include <stdio.h>

int main(void)
{
    char cNum = 65;
    char *p_cNum = &cNum;
    printf("p_cNum 크기: %ld Byte\n", sizeof p_cNum);
    printf("p_cNum 포인터 변수 값: %p\n", p_cNum);
    printf("p_cNum 포인터 변수 역참조 값: %d\n", *p_cNum);
    printf("cNum 값: %d\n\n", cNum);

    return 0;
}

/* 결과
p_cNum 크기: 8 Byte
p_cNum 포인터 변수 값: 0x7ffeacd2eb5f
p_cNum 포인터 변수 역참조 값: 65
cNum 값: 65 
cNum 크기: 1 Byte */

 


예제2) int 포인터 변수의 역참조

#include <stdio.h>

int main(void)
{
    int iNum = 65;
    int *p_iNum = &iNum;
    printf("p_iNum 크기: %ld Byte\n", sizeof p_iNum);
    printf("p_iNum 포인터 변수 값: %p\n", p_iNum);
    printf("p_iNum 포인터 변수 역참조 값: %d\n", *p_iNum);
    printf("iNum 값: %d\n", iNum);
    printf("iNum 크기: %ld Byte\n", sizeof iNum);

    return 0;
}

/* 결과
p_iNum 크기: 8 Byte
p_iNum 포인터 변수 값: 0x7fffa782d1bc
p_iNum 포인터 변수 역참조 값: 65
iNum 값: 65
iNum 크기: 4 Byte */

 


7. 포인터 관련 연산자의 논리 구조

 

더보기

 

포인터는 역참조를 위해 존재한다.
포인터의 역참조는 일반 변수 사용과 동일한 기능이다.

 

 

7.1 포인터 관련 연산자의 논리 구조

 

 

기호 발음 의미 해석
Ampersand, 앰퍼센드 주소 연산자 변수명과 함께 사용한다.
변수가 참조하는 메모리 공간의 첫 Byte 주소
* Asterisk, 애스터리스크
참조 연산자 1) 포인터 변수 선언시 사용 
2) 포인터 변수를 역참조하여 데이터에 접근할 때 사용

 

 

개념 예시 포인터는 "가르킨다"는 의미다.
변수 → 데이터 iNum → 90  일반 변수 사용 방법
변수 + & → 포인터 + * → 데이터 iNum + & → p_iNum + * → 90  
&변수 → 포인터 (&iNum) → p_iNum 메모리 주소
*포인터 → 데이터 *p_iNum → 90 메모리 주소의 데이터를 포인터한다.
*(&변수) → 데이터 *(&iNum) → 90 포인터 역참조는
변수의 사용과 동일한 방법이다.
변수 → 데이터 iNum → 90

 

 

 

 

7.2 예제

 

 int iHexNum = 0x1a2b3c4d;

 

 

#include <stdio.h>
 
void main()
{
    //  int 범위: -2,147,483,648 ~ 2,147,483,647
    //  16진수:1a2b3c4d == 10진수:   439,041,101

    //1.
    int iHexNum = 0x1a2b3c4d;
    printf("1. int 자료형 변수 iHexNum에 16진수 0x1a2b3c4d 값을 저장\n");
    printf("iHexNum size: %ld Byte, int iHexNum = %x;\n\n", sizeof iHexNum, iHexNum);
    
    //2.
    char* p_chr = (char*)&iHexNum;
    int*  p_int = &iHexNum;
    printf("2. char 형 포인터 변수 prt1, int 형 포인터 변수 prt2 크기 출력\n");
    printf("char* p_chr 크기 = %ld Byte;\nint*  p_int 크기 = %ld Byte;\n\n", sizeof p_chr, sizeof p_int);

    // 3. 기본 자료형 변수와, 포인터 변수 사용
    printf("3. 기본 자료형 변수, 포인터 변수 모두 \"변수명\"만 사용합니다.\n");
    printf("iHexNum: %x\n", iHexNum);
    printf("p_chr   : %p\n\n", p_chr);

    // 4. 역참조
    printf("4. 포인터 변수는, *(역참조 연산자) 사용이 가능합니다.\n");
    printf("   포인터 변수 선언시 사용한 자료형은, 역참조 시 가져올 Byte 크기를 결정합니다.\n\n");
    
    printf("char형 포인터 변수 p_chr 역참조\n");
    printf("*p_chr = %x;\n", *p_chr);
    printf("p_chr 포인터 변수명 앞에 *(역참조 연산자)를 사용합니다.\n");
    printf("최초 선언한 int iHexNum = 0x1a2b3c4d 의 메모리 주소에서 char 형 크기 1Byte 만 역참조 합니다.\n\n");

    printf("int 형 포인터 변수 p_int 역참조\n");
    printf("*p_int = %x;\n", *p_int);
    printf("p_int 포인터 변수명 앞에 *(역참조 연산자)를 사용합니다.\n");
    printf("최초 선언한 int iHexNum = 0x1a2b3c4d 의 메모리 주소에서 int 형 크기 4Byte 만 역참조 합니다.\n\n");
}

 

 

 


7.2 포인터 역참조 개념 정리

  • 포인터는 "가리킨다" 는 역참조의 의미다. ( ☞ 메모리 공간)
  • 포인터 변수는 "메모리 주소 값"을 저장하는 변수다.
  • 포인터 변수는 *역참조(포인터, "가리킨다")를 위해 존재한다.
  • 포인터 변수라고 불리는 의미에 대해 생각하고 사용한다.
  • 포인터 변수의 역참조는 일반 변수의 사용과 동일한 기능이다. 

 

 

8. 포인터는 역참조를 위해 존재한다.

 

더보기

 

포인터는 역참조를 위해 존재한다.

포인터다.

 

#include <stdio.h>

void main()
{
    int iNum = 90;

    int *p_iNum = &iNum; 
    printf("p_iNum 포인터 변수 값: %p\n", p_iNum); // 포인터 변수 사용

    int iVar = *p_iNum;                 // 포인터 변수 역참조는 일반 변수의 기능과 동일하다.
    printf("iVar 변수 값: %d\n", iVar); // 포인터 변수는 역참조를 위해 존재한다
}

 

 

9. 포인터 변수 역참조 연산

 

더보기

 

9.1 기준 예제

 

 int iHexNum = 0x1a2b3c4d;

 

 

#include <stdio.h>

void main()
{
    int iHexNum = 0x1a2b3c4d;
    int *ptr = &iHexNum;
    printf("ptr 포인터 변수 값: %p\n", ptr); // 포인터 변수 사용

    int iNum = *ptr;
    printf("iNum 정수형 변수 값: %d\n", iNum);
}

 

 

 

 

9.2 포인터 변수 선언과 사용(2) - 포인터 변수의 특성, 세번째 - 역참조 자료형

 

#include <stdio.h>

void main()
{
    int iHexNum = 0x1a2b3c4d;
    char *ptr = (char*)&iHexNum;
    printf("ptr 포인터 변수 값: %p\n", ptr); // 포인터 변수값 동일
    
    char cNum = *ptr;
    printf("cNum 정수형 변수 값: %d\n", cNum);
    printf("cNum 정수형 변수 값: %x\n", cNum);
    
    // 그 외 모두 가능하다.
    long *lNum = (char*)&iHexNum;
    double *fNum = (double*)&iHexNum;
}

 

포인터 변수는, 선언시 사용하는 역참조 자료형이 char, int, long 에 상관없이 첫번째 Byte 주소값이 담긴다.

즉 어떤 주소값이든, char 형 포인터 변수, int 형 포인터 변수, long 형 포인터 변수 등 모두 저장 가능하다.

 

포인터는 포인터다.

포인터 변수명은 참조하는 공간의 첫번째 Byte 주소값을 담아 사용하는 것이 목적이다.

 

 

 


9.3 포인터 변수 선언과 사용(2) - 포인터 변수의 특성, 네번째 - 역참조 연산

  

 

메모리 주소는 순차적으로 연속되며, 중복되지 않은 숫자값이다.

숫자값은 사칙연산이 가능하다.

 

#include <stdio.h>

void main()
{
    int iHexNum = 0x1a2b3c4d;
    char *ptr = (char*)&iHexNum;
    printf("ptr 포인터 변수 값: %p\n", ptr); // 포인터 변수값 동일
    
    printf("ptr + 0 == %p\n", ptr + 0);
    printf("ptr + 1 == %p\n", ptr + 1);
    printf("ptr + 2 == %p\n", ptr + 2);
    printf("ptr + 3 == %p\n", ptr + 3);

    
    printf("*(ptr + 0) == %x\n", *(ptr + 0));
    printf("*(ptr + 1) == %x\n", *(ptr + 1));
    printf("*(ptr + 2) == %x\n", *(ptr + 2));
    printf("*(ptr + 3) == %x\n", *(ptr + 3));

    char arr[4];
    arr[0] = *(ptr + 0);
    arr[1] = *(ptr + 1);
    arr[2] = *(ptr + 2);
    arr[3] = *(ptr + 3);
    
    printf("arr[0] == %x\n", arr[0]);
    printf("arr[1] == %x\n", arr[1]);
    printf("arr[2] == %x\n", arr[2]);
    printf("arr[3] == %x\n", arr[3]);
}

 

 

 

 

예제)

 

// C 만큼은 IDE 도움없는 개발 상황과, 고대 레거시 코드에 대비하여 
// 고대 형식의 헝가리안 표기법을 (쓰지 않더라도) 알아야한다.

#include <stdio.h>
 
void main()
{
    //  int 범위: -2,147,483,648 ~ 2,147,483,647
    //  16진수:1a2b3c4d == 10진수:   439,041,101
    //  16진수:7fffffff == 10진수: 2,147,483,647

    //1. 16진수 0x1a2b3c4d 값을, int 자료형 변수 iHexNum에 저장
    int iHexNum = 0x1a2b3c4d; // int int_max = 0x7fffffff;
    printf("1. 16진수 0x1a2b3c4d 값을, int 자료형 변수 iHexNum에 저장\n");
    printf("iHexNum size: %ld Byte, int iHexNum = %x;\n\n", sizeof iHexNum, iHexNum);
    
    
    // 2. iHexNum 변수에 대입한 16진수 0x1a2b3c4d 값을 출력
    printf("2. iHexNum 값 10진수, 16진수 출력\n");
    printf("iHexNum 변수의 10진수 값 = %d, 16진수 값 = %x;\n\n", iHexNum, iHexNum);

    // 3 포인터의 크기 출력
    char* ptr1 = (char*)&iHexNum;
    int*  ptr2 = &iHexNum;
    printf("3. iHexNum 변수 주소가 할당된,\n   int형 포인터 변수 prt1, char 형 포인터 변수 prt2 크기 출력\n");
    printf("char* ptr1 크기 = %ld Byte;\nint*  ptr2 크기 = %ld Byte;\n\n", sizeof ptr1, sizeof ptr2);
 
 
    // 4. 포인터 변수 ptr 위치에 들어있는 값을 1바이트씩 16진수로 출력(1)
    //    포인터 (연산)
    char* ptr = (char*)&iHexNum;
    printf("4. char* 포인터 변수 ptr 에 iHexNum 변수의 주소값을 저장;\n");
    printf("char* ptr = &iHexNum;\n");
    printf("char* ptr = %p;\n\n", ptr);

    printf("4-1. char* 포인터 변수 ptr 주소값 연산;\n");
    printf("ptr + 0 == %p\n", ptr + 0);
    printf("ptr + 1 == %p\n", ptr + 1);
    printf("ptr + 2 == %p\n", ptr + 2);
    printf("ptr + 3 == %p\n", ptr + 3);
 
    printf("\n4-2. char* 포인터 변수 ptr 주소값 연산 후, 1바이트씩 출력\n");
    printf("*(ptr+0) == %x\n", *(ptr + 0));
    printf("*(ptr+1) == %x\n", *(ptr + 1));
    printf("*(ptr+2) == %x\n", *(ptr + 2));
    printf("*(ptr+3) == %x\n", *(ptr + 3));

    printf("\n4-3. 위 소스코드와 * 포인터 연산자 위치 비교\n");
    printf("(*ptr+0) == %x\n", (*ptr + 0));
    printf("(*ptr+1) == %x\n", (*ptr + 1));
    printf("(*ptr+2) == %x\n", (*ptr + 2));
    printf("(*ptr+3) == %x\n", (*ptr + 3));
 
    // 5. 포인터 변수 ptr 에 들어있는 값을 1바이트씩 16진수로 출력(2)
    //    포인터 [배열 인덱스]
    printf("\n5. 포인터 변수 ptr 에 들어있는 1바이트씩 출력\n");
    printf("ptr[0] == %x\n", ptr[0]);
    printf("ptr[1] == %x\n", ptr[1]);
    printf("ptr[2] == %x\n", ptr[2]);
    printf("ptr[3] == %x\n", ptr[3]);

    // ----------------------------------------------------------------
    
    // 6. 포인터 변수 ptr 위치에 들어있는 값을 1바이트씩 16진수로 출력(1)
    //    포인터 (연산)
    int* ptr_int = &iHexNum;
    printf("4. int* 포인터 변수 ptr 에 iHexNum 변수의 주소값을 저장;\n");
    printf("int* ptr = &iHexNum;\n");
    printf("int* ptr = %p;\n\n", ptr_int);

    printf("4-1. int* 포인터 변수 ptr 주소값 연산;\n");
    printf("ptr_int + 0 == %p\n", ptr_int + 0);
    printf("ptr_int + 1 == %p\n", ptr_int + 1);
    printf("ptr_int + 2 == %p\n", ptr_int + 2);
    printf("ptr_int + 3 == %p\n", ptr_int + 3);
 
    printf("\n4-2. int* 포인터 변수 ptr_int 주소값 연산 후, 4바이트씩 출력\n의도하지 않은 위치의 의도하지 않은 값 출력\n");
    printf("*(ptr_int+0) == %x\n", *(ptr_int + 0));
    printf("*(ptr_int+1) == %x\n", *(ptr_int + 1));
    printf("*(ptr_int+2) == %x\n", *(ptr_int + 2));
    printf("*(ptr_int+3) == %x\n", *(ptr_int + 3));

    printf("\n4-3. 위 소스코드와 * 포인터 연산자 위치 비교\n");
    printf("(*ptr_int+0) == %x\n", (*ptr_int + 0));
    printf("(*ptr_int+1) == %x\n", (*ptr_int + 1));
    printf("(*ptr_int+2) == %x\n", (*ptr_int + 2));
    printf("(*ptr_int+3) == %x\n", (*ptr_int + 3));
 
    // 5. 포인터 변수 ptr 에 들어있는 값을 1바이트씩 16진수로 출력(2)
    //    포인터 [배열 인덱스]
    printf("\n5. 포인터 변수 ptr_int 에 들어있는 4바이트씩 출력\n의도하지 않은 위치의 의도하지 않은 값 출력\n");
    printf("ptr_int[0] == %x\n", ptr_int[0]);
    printf("ptr_int[1] == %x\n", ptr_int[1]);
    printf("ptr_int[2] == %x\n", ptr_int[2]);
    printf("ptr_int[3] == %x\n", ptr_int[3]); 
}

 

 

 

 * int 자료형의 MAX 값인 2,147,483,647 정수를 입력하고 확인해보자.

 


정리

  • 메모리 에서 데이터가 메모리에 어떻게 저장되는지 이해해야 한다.
  • 데이터가 메모리에 저장되는 공간을 머리속에 그릴 수 있어야 한다.
  • 자료형에 따라 메모리의 저장 공간 크기가 달라지는 것을 이해해야 한다.
  • 포인터 변수는 메모리 주소값을 저장하는 용도다.
  • 포인터는 역참조를 의미하고, 포인터 변수는 메모리 주소를 통해 "역참조"하기 위해 존재한다.