Ch 07. 포인터 - 06. void pointer

2025. 4. 15. 20:55Programming Language/C++

반응형

1. void poitner

void pointer는 어떤 타입의 데이터든 가리킬 수 있는 범용 포인터로 int*, double*, char*처럼 특정 타입을 가리키는 포인터가 아니라 타입이 없는 포인터로 어떤 메모리든 가리킬 수 있다.

void* ptr;
int a = 10;
ptr = &a;  // int형 변수의 주소를 void 포인터로 저장

 

2. 왜 void pointer 가 필요할까

void 포인터가 필요한 이유는 여러가지가 있는데 간단하게 몇가지 말해보자면

  • 다양한 타입을 하나의 인터페이스로 받기 위해(어떤 타입이 올지 모르는 경우 void 포인터로 받아 놓고 나중에 처리할 수 있게 된다)
  • C라이브러리와의 호환성을 위해(C API는 타입 정보가 없어서 주로 void*를 사용한다)
더보기

C언어의 경우는 템플릿이 없었기에 타입을 컴파일 타임에 타입을 알 수 없었기에 void*와 해당 타입에 대한 정보를 플래그로써 전달하면서 사용 했어야 했기에

// C 언어에서는 이렇게 썼음
void print(void* data, int type) {
    if (type == 0)       // 정수
        printf("%d\n", *(int*)data);
    else if (type == 1)  // 실수
        printf("%f\n", *(double*)data);
}

이런식으로 사용했어야만 했다.

 

물론 템플릿 뿐만 아니라 함수의 오버로딩도 존재하지 않기에 타입별로 함수가 값을 받아줄 수가 없어 void 포인터가 필수적이 였다.

 

C++의 경우는 함수 오버로딩, 템플릿이 존재하기에 아래와 같은 방식을 사용하면 궂이 void 포인터를 사용할 필요는 없으나

 

[함수 오버로딩으로 처리]

#include <iostream>

void print(int data) {
    std::cout << data << std::endl;
}

void print(double data) {
    std::cout << data << std::endl;
}

 

[템플릿으로 처리]

#include <iostream>

template<typename T>
void print(T data) {
    std::cout << data << std::endl;
}

 

C++에서도 이런 C의 라이브러리를 사용하는 경우가 매우 많기에 그 호환성을 위해서 void 포인터를 사용할 수 밖에 없게 된다.

등등의 이유가 존재한다.

 

 

더보기

C에서 void 포인터를 사용하는 것들

 

1. malloc 함수

c언어에서 동적할당을 공부할때 malloc 함수라는것을 

이렇게 사용했었는데 보다 싶이 malloc 함수의 경우는 사이즈 또한 사용자가 설정하고 어떤 타입으로 값을 반환해야할지에 대해서 정해지지 않은 함수이다.

 

이럴때 이 함수는 반환타입을 지정할 수 가 없기에 void * 를 사용하게 되는데

이 또한 C의 코드를 사용하는 C++의 입장에서는 void포인터를 사용해야할 수 밖에 없는 이유가 된다.

 

2. free() 함수

free 함수도 포인터를 매개변수로 전달할때 모든 매개변수를 받아줄 수 있어야 하기 때문에 

매개변수의 타입이 void 포인터로 밖에 설정할 수 가 없게 된다.

그래서 이 경우에도 void 포인터가 사용된다는 점..

 

3. void 포인터의 사용방법

기존의 포인터의 경우는 타입이 다른 값의 주소값을 저장하려고 한다면 컴파일러에서 너 잘못된 행동하고 있어 라고 하면서 에러를 띄워줬었다.

 

반면 void 포인터의 경우는 어떤 포인터도 받아줄 수 있는 포인터로

이렇게 다른 타입의 포인터의 경우(주소값의 경우)도 void포인터에 담아줄 수 있게 된다.

 

또 포인터에 포인터의 값을 저장할 경우에도 타입이 맞지 않으면 에러가 발생했었다.

이 경우에도 동일하게 void 포인터의 경우는 

문제 없이 받아준다.

 

반면 void 포인터의 경우는 역참조를 하는것이 불가능하다.

왜 냐면 포인터의 경우 그 타입이 지정되어 있는 이유는 해당 주소에 있는 값을 컴파일러로 하여금 어떤 타입의 형태로 읽어 들일 것인지에 대한 정보를 제공해주기 위함인데

float fnum = 3.1415;
float* fptr = &fnum;

std::cout << *fptr << std::endl; // fptr이 가리키는 주소의 내부 값을 float형으로 읽어옴

void 포인터의 경우는 어떤 타입인지를 전혀 알고 있지 않기 때문에 해당 주소값에 있는 값들을 처리할 수 없기 때문이다.

float fnum = 3.1415;
void* vptr = &fnum;

// fptr이 가리키는 주소의 내부 값을 vptr은 어떻게 읽어야 할지에 대한 정보가 존재하지 않음
std::cout << *vptr << std::endl;

 

다행히 void포인터의 경우 다행하게도 간단하게 포인터에 형변환을 해주면서 다른 포인터로 전환이 가능하다.

이렇게 void 포인터의 내부에 존재하는 값을 읽고자하는 타입의 포인터로 전환해주면 역참조가 가능해진다.

물론 

이렇게도 사용이 가능하다.

 

 

추가로 배열을 저장하는 포인터로써 void 포인터가 사용된다면

이 경우엔 기존에 배열을 포인터로 받았을때 사용했던것 처럼 

이렇게 사용하는것도 불가능하다

이 또한 결국

이거랑 동일한데 이전에 말했다 싶이 뒤에 붙는 숫자는 앞의 포인터의 타입의 크기만큼 +1이 되면 타입의 크기 * 1의 크기만큼, +2가 된다면 타입의 크기 * 2의 크기만큼 뒷 주소를 바라보도록 하는 것인데 여기 또한 타입에 대한 정보가 없기에 이런 사용방법도 불가능하다.

이를 그대로 사용하고자 한다면

이렇게 void 포인터에 타입정보를 전달해줘야만 한다.

 

 

반응형