31.5 void 포인터 선언하기

long long *numPtr1;이나 float *numPtr2;는 자료형이 정해진 포인터입니다. 하지만 C 언어에서는 자료형이 정해지지 않은 포인터도 있습니다. void 포인터라는 포인터인데 다음과 같이 void 키워드와 *로 선언합니다.

  • void *포인터이름;

void_pointer.c

#include <stdio.h>

int main()
{
    int num1 = 10;
    char c1 = 'a';
    int *numPtr1 = &num1;
    char *cPtr1 = &c1;

    void *ptr;        // void 포인터 선언

    // 포인터 자료형이 달라도 컴파일 경고가 발생하지 않음
    ptr = numPtr1;    // void 포인터에 int 포인터 저장
    ptr = cPtr1;      // void 포인터에 char 포인터 저장

    // 포인터 자료형이 달라도 컴파일 경고가 발생하지 않음
    numPtr1 = ptr;    // int 포인터에 void 포인터 저장
    cPtr1 = ptr;      // char 포인터에 void 포인터 저장

    return 0;
}

기본적으로 C 언어는 자료형이 다른 포인터끼리 메모리 주소를 저장하면 컴파일 경고(warning)가 발생합니다. 하지만 void 포인터는 자료형이 정해지지 않은 특성 때문에 어떤 자료형으로 된 포인터든 모두 저장할 수 있습니다. 반대로 다양한 자료형으로 된 포인터에도 void 포인터를 저장할 수도 있습니다. 이런 특성 때문에 void 포인터는 범용 포인터라고 합니다.

즉, 직접 자료형을 변환하지 않아도 암시적으로 자료형이 변환되는 방식입니다.

// 포인터 자료형이 달라도 컴파일 경고가 발생하지 않음
ptr = numPtr1;    // void 포인터에 int 포인터 저장
ptr = cPtr1;      // void 포인터에 char 포인터 저장

// 포인터 자료형이 달라도 컴파일 경고가 발생하지 않음
numPtr1 = ptr;    // int 포인터에 void 포인터 저장
cPtr1 = ptr;      // char 포인터에 void 포인터 저장
그림 31-24 void 포인터

단, void 포인터는 자료형이 정해지지 않았으므로 값을 가져오거나 저장할 크기도 정해지지 않았습니다. 따라서 void 포인터는 역참조를 할 수 없습니다.

ptr = numPtr1;        // void 포인터에 int 포인터 저장
printf("%d", *ptr);   // void 포인터는 역참조할 수 없음. 컴파일 에러

ptr = cPtr1;          // void 포인터에 char 포인터 저장
printf("%c", *ptr);   // void 포인터는 역참조할 수 없음. 컴파일 에러

컴파일 결과

error C2100: 간접 참조가 잘못되었습니다.
error C2100: 간접 참조가 잘못되었습니다.

void 키워드로는 변수를 선언할 수도 없습니다.

void v1;    // void로는 변수를 선언할 수 없음. 컴파일 에러

컴파일 결과

error C2182: 'v1': 'void' 형식을 잘못 사용했습니다.

그런데 역참조도 할 수 없는 void 포인터는 왜 사용할까요? void 포인터는 되는 게 별로 없어 보이지만 실제로 C 언어에서 다양한 형태로 사용되고 있습니다. 예를 들자면 함수에서 다양한 자료형을 받아들일 때, 함수의 반환 포인터를 다양한 자료형으로 된 포인터에 저장할 때, 자료형을 숨기고 싶을 때 사용합니다.

아무래도 void 포인터는 초보자가 이해하기 힘든 부분이라 여기서는 완벽하게 이해하지 않고 넘어가도 됩니다. 'Unit 43 자료형 변환하기', 'Unit 46 함수에서 반환값 사용하기', 'Unit 48 함수에서 포인터 매개변수 사용하기'에서 자세히 설명하겠습니다.