포인터

김민수·2025년 1월 8일

C++

목록 보기
26/68

포인터 변수: 메모리의 주소를 저장하는 변수
포인터를 통해 특정 메모리 주소로 이동해서 데이터를 참조하거나 수정할 수 있음


1. 포인터 선언

int* pInt; // 정수를 가리키는 포인터 변수
char* pChar; // 문자를 가리키는 포인터 변수
  • 자료형 + * 변수명
  • 포인터 변수의 자료형은 포인터가 가리키는 메모리의 데이터를 어떤 타입으로 해석할지를 정의하는 것
  • 예: int*는 주소를 int로 해석하고, char*는 주소를 char로 해석


2. 포인터의 동작

  1. 포인터 변수 선언 및 초기화

    int a = 10;
    int* pInt = &a; // a의 주소를 pInt에 저장
    • & 연산자: 변수의 주소를 가져옴
    • pInta의 주소를 저장하고, 이 주소를 통해 a의 값을 참조할 수 있음
  2. 포인터를 통한 값 접근

    *pInt = 20; // 포인터를 통해 a의 값을 20으로 변경
    printf("%d\n", a); // 출력: 20
    • * 연산자: 포인터가 가리키는 주소의 값을 참조


3. 메모리 주소와 포인터

  • 메모리 주소는 바이트 단위로 저장됨
    • 예: 주소값이 100, 101, 102라면 이는 메모리 상에서 연속적인 1바이트 간격으로 나타냄
  • 포인터는 메모리 주소를 저장하며, 이를 통해 특정 메모리 위치에 접근함
  • 포인터의 타입에 따라 주소 증가 단위가 달라짐
    • 예: int* 포인터는 4바이트(sizeof(int)) 단위로 이동
  • pInt += 1은 다음 int 위치로 이동하며, 주소값은 sizeof(int)만큼 증가
int arr[3] = {10, 20, 30};
int* pInt = arr;

std::cout << "초기 주소: " << pInt << std::endl; // arr[0]의 주소
pInt += 1; // 다음 int로 이동
std::cout << "1 증가 후 주소: " << pInt << std::endl; // arr[1]의 주소
std::cout << "1 증가 후 값: " << *pInt << std::endl; // 출력: 20


4. 포인터 변수의 크기

포인터 변수의 크기는 포인터가 가리키는 자료형과는 무관하며, 플랫폼(운영체제)에 따라 결정

  • 32비트 운영체제: 포인터 크기 = 4바이트
  • 64비트 운영체제: 포인터 크기 = 8바이트
int* pInt;
char* pChar;

printf("%zu\n", sizeof(pInt)); // 4 (32비트) 또는 8 (64비트)
printf("%zu\n", sizeof(pChar)); // 동일한 크기


5. 배열의 특징

  1. 메모리가 연속적인 구조이다

    • 예를 들어, int iArr[10] 선언 시, 배열 iArr는 10개의 정수를 저장할 수 있는 연속된 메모리 공간을 할당받음
    • 각 요소는 sizeof(iArr[0])만큼의 간격을 가짐
  2. 배열의 이름은 배열의 시작 주소이다

    • 예: int iArr[10];에서 iArr&iArr[0]와 동일한 의미
int iArr[10] = {};
*(iArr + 0) = 10; // 포인터 연산을 이용하여 iArr[0]에 접근
*iArr = 10;       // 배열 이름 자체가 시작 주소이므로 동일한 표현


6. 포인터 문제 풀이 - 1

short sArr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pI = (int*)sArr;
int iData = *((short*)(pI + 2));
printf("1번 문제 정답 : %d\n", iData);

풀이
1. short sArr[10] 배열 선언

  • 배열 sArrshort 타입(2바이트) 요소 10개로 구성
  • 메모리 배치
    주소    값 (2진수로 표현)
    0x00    00000000 00000001 (sArr[0] = 1)
    0x02    00000000 00000010 (sArr[1] = 2)
    0x04    00000000 00000011 (sArr[2] = 3)
    0x06    00000000 00000100 (sArr[3] = 4)
    0x08    00000000 00000101 (sArr[4] = 5)
    (주소는 2바이트 단위로 증가)
  1. int* pI = (int*)sArr

    • sArrint*로 강제 캐스팅했기 때문에, pI는 4바이트(2개의 short) 단위로 배열을 접근
    • 결과적으로:
      pI[0] = 00000000 00000001 | 00000000 00000010 (12 합침)
      pI[1] = 00000000 00000011 | 00000000 00000100 (34 합침)
      pI[2] = 00000000 00000101 | 00000000 00000110 (56 합침)
  2. pI + 2 접근

    • pI + 2sArr[4]sArr[5]를 포함한 4바이트 영역을 가리킴
  3. (short*)(pI + 2)로 캐스팅

    • pI + 2short*로 캐스팅하면, 다시 2바이트 단위로 접근하게 됨
    • 즉, *((short*)(pI + 2))sArr[4]를 가리킴

결과

  • 5 출력


7. 포인터 문제 풀이 - 2

char cArr[2] = { 1, 1 };
short* pS = (short*)cArr;
iData = *pS;
printf("2번 문제 정답 : %d\n", iData);

풀이

  1. char cArr[2] 배열 선언

    • 배열 cArrchar 타입(1바이트) 요소 2개로 구성
    • 메모리 배치
      주소    값 (2진수로 표현)
      0x00    00000001 (cArr[0] = 1)
      0x01    00000001 (cArr[1] = 1)
  2. short* pS = (short*)cArr

    • cArrshort*로 캐스팅했기 때문에, pS는 2바이트 단위로 배열을 접근
    • pScArr[0]cArr[1]를 합친 값을 가리킵니다.
  3. *pS 접근

    • pS가 가리키는 2바이트는 다음과 같이 결합:
      00000001 | 00000001 (cArr[0]과 cArr[1] 결합)
      = 00000001 00000001 (2진수)
      = 257 (10진수)

결과

  • 257 출력


8. cArr 배열이 {1, 2}로 변경되었을 때 결과가 258이 아니라 513인 이유

  • C 언어는 대부분의 현대 컴퓨터에서 리틀 엔디언 방식을 따름
  • 따라서 cArr[0] 값이 하위 바이트로 먼저 저장되고, cArr[1] 값이 상위 바이트로 저장됨
  • short* pS로 읽을 때, 메모리에서 하위 바이트부터 읽기 때문에 값이 513이 됨

  1. 엔디언(Endian) 방식이란?

    • 엔디언: 멀티바이트 데이터를 메모리에 저장할 때, 바이트의 순서를 정하는 방식
    • 데이터를 메모리에 저장할 때, 시스템의 엔디언 방식을 따름

    리틀 엔디언 (Little Endian)

    • 가장 작은 단위(LSB, Least Significant Byte)가 먼저 저장됨
    • 예: 0x0201 (16진수 값) → 01 02 (메모리에 저장되는 순서)

    빅 엔디언 (Big Endian)

    • 가장 큰 단위(MSB, Most Significant Byte)가 먼저 저장됨
    • 예: 0x020102 01

  2. cArr[2] = {1, 2}의 메모리 배치

배열 선언

char cArr[2] = {1, 2};
short* pS = (short*)cArr;
int iData = *pS;

메모리 상태 (리틀 엔디언 기준)

주소    값
0x00    00000001 (cArr[0] = 1)
0x01    00000010 (cArr[1] = 2)

short* pS 접근

  • 리틀 엔디언일 때
바이트 순서: 00000010 | 00000001
            (cArr[1])  (cArr[0])
  • 결과: 0x0201 (16진수) = 513 (10진수)

  • 빅 엔디언일 때

바이트 순서: 00000001 | 00000010
            (cArr[0])  (cArr[1])
  • 결과: 0x0102 (16진수) = 258 (10진수)
profile
안녕하세요

0개의 댓글