43.2 포인터 변환하기

이번에는 포인터끼리 변환하는 방법입니다. 이때는 자료형 뒤에 포인터를 나타내는 *(애스터리스크)를 붙여주고 괄호로 묶어주면 됩니다.

  • (자료형 *)포인터

다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

type_conversion_pointer.c

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int *numPtr = malloc(sizeof(int));    // 4바이트만큼 메모리 할당
    char *cPtr;

    *numPtr = 0x12345678;

    cPtr = (char *)numPtr;     // int 포인터 numPtr을 char 포인터로 변환. 메모리 주소만 저장됨

    printf("0x%x\n", *cPtr);   // 0x78: 낮은 자릿수 1바이트를 가져오므로 0x78

    free(numPtr);    // 동적 메모리 해제

    return 0;
}

실행 결과

0x78

numPtr에 메모리를 할당하고 역참조하여 0x12345678을 저장했습니다. 그리고 cPtr = (char *)numPtr;과 같이 int 포인터를 char 포인터로 변환하여 메모리 주소를 저장했습니다.

이 상태에서 printf*cPtr을 출력해보면 어떻게 될까요? *cPtr과 같이 역참조하면 numPtr의 메모리 주소에 접근하지만 cPtr은 char 포인터이므로 1바이트만큼만 값을 가져옵니다. 즉, numPtr이나 cPtr이나 안에 저장된 메모리 주소는 같지만 자료형에 따라 역참조했을 때 값을 가져오는 크기가 결정되기 때문입니다.

그림 43-2 포인터 변환

여기서는 메모리 공간에 0x12345678(리틀 엔디언: 78 56 34 12)이 저장된 상태에서 1바이트 char 크기만큼 낮은 자릿수 값을 가져오므로 0x78이 됩니다. 자료형이 다른 포인터에 메모리 주소가 이리저리 옮겨 다닌다고 해도 메모리 공간은 그대로이고, 값을 가져오는 크기만 달라진다는 점을 기억하세요.

앞의 예제와는 반대로 크기가 작은 메모리 공간을 할당한 뒤 큰 자료형의 포인터로 역참조하면 의도치 않은 값을 가져오게 됩니다.

type_conversion_pointer_invalid_value.c

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    short *numPtr1 = malloc(sizeof(short));    // 2바이트만큼 메모리 할당
    int *numPtr2;

    *numPtr1 = 0x1234;

    numPtr2 = (int *)numPtr1;    // short 포인터 numPtr1을 int 포인터로 변환. 메모리 주소만 저장됨

    printf("0x%x\n", *numPtr2);    // 0xfdfd1234: 옆의 메모리를 침범하여 값을 가져옴
                                   // 0xfdfd는 상황에 따라서 값이 달라질 수 있음

    free(numPtr1);    // 동적 메모리 해제

    return 0;
}

실행 결과

0xfdfd1234

이번에는 numPtr1에 2바이트 크기로 메모리를 할당하고 역참조하여 0x1234를 저장했습니다. 그리고 numPtr2 = (int *)numPtr1;과 같이 short 포인터를 int 포인터로 변환하여 메모리 주소를 저장했습니다.

printf*numPtr2를 출력해보면 1234 앞에 엉뚱한 값이 붙어서 나왔습니다. 크기가 작은 메모리를 할당한 뒤 큰 자료형의 포인터로 역참조하면 옆의 메모리 공간을 침범하여 값을 가져오게 됩니다.

그림 43-3 포인터를 큰 자료형으로 역참조하여 메모리 공간 침범

malloc 함수로 2바이트만큼 메모리를 할당했으므로 0x1234(리틀 엔디언: 34 12)만 저장되어 있습니다. 하지만 이 상태에서 4바이트 int 크키만큼 값을 가져오면 2바이트 크기를 벗어나서 malloc 함수로 할당하지 않은 공간까지 함께 가져오게 됩니다. 따라서 의도치 않은 값을 가져오게 되므로 주의해야 합니다. 참고로 할당되지 않은 공간에는 쓰레기 값이 들어있습니다.

참고 | 포인터 변환과 컴파일 경고

일반 변수와 마찬가지로 포인터도 변환할 때 자료형이 다르면 컴파일 경고가 발생합니다.

int *numPtr = malloc(sizeof(int));    // int 포인터
char *cPtr;       // char 포인터

cPtr = numPtr;    // 컴파일 경고 발생

free(numPtr);

컴파일 결과

warning C4133: '=': 'int *'과(와) 'char *' 사이의 형식이 호환되지 않습니다.

포인터를 다른 자료형으로 변환하면서 역참조하려면 다음과 같이 ( ) (괄호) 앞에 역참조 연산자 *를 붙여주면 됩니다.

  • *(자료형 *)포인터

type_conversion_pointer_dereference.c

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int *numPtr = malloc(sizeof(int));    // 4바이트만큼 메모리 할당
    char *cPtr;

    *numPtr = 0x12345678;

    printf("0x%x\n", *(char *)numPtr);    // 0x78: numPtr1을 char 포인터로 변환한 뒤 역참조

    free(numPtr);    // 동적 메모리 해제

    return 0;
}

실행 결과

0x78

*(char *)numPtr과 같이 char 포인터로 변환한 뒤 역참조 했습니다. 따라서 0x12345678에서 1바이트 char 크기만큼 낮은 자릿수 값 0x78을 가져옵니다.