[C++] void 포인터의 개념과 사용법

void 포인터가 무엇인가?

void 포인터는 데이터 타입에 구애받지 않는, 메모리를 가리키는 포인터입니다.

 

포인터의 선언은 다음과 같습니다.

데이터_타입* 변수명

 

이 "데이터 타입"에 void가 쓰여서, 정해진 데이터 타입이 없다는 것을 나타냅니다.

char c;
int* pInt = &c;		// compile error !!
void* pVoid = &c;	// ok

위에서 char 포인터를 int 포인터에 대입하면 데이터 타입 불일치로 컴파일이 되지 않습니다.

그러나, void 포인터는 메모리 주소이기만 하면 전혀 데이터 타입을 고려하지 않습니다.

char*, 배열 포인터, 함수 포인터 등등 주소를 가리키는 것은 다 담을 수 있죠.

 

하지만, 단점도 있습니다.

우선, 역참조 연산자 *를 사용할 수 없습니다.

void 포인터가 가리키는 데이터 타입을 알 수 없기 때문이죠.

포인터 사용의 목적의 하나인 가리키는 데이터를 읽거나 사용을 할 수 없습니다.

 

또한,  void 포인터를 대상으로 +, -, ++ 등의 연산을 수행하면 위와 마찬가지 이유로 컴파일 경고를 만나게 될 것입니다.

int a = 10;
void* p = &a;
p++;	// compile warning !!

 

그래서, void 포인터가 가리키는 데이터를 사용하기 위해서는, 적절한 타입으로 형 변환을 해야 합니다.

int genericFunc( void* pVoid ){
	
    int* pInt = (int*)pVoid;    // 포인터 타입 변환이 필요합니다.
    return *pInt;	
}

int main(){

    int a = 0;
    void* pVoid = &a;
    
    int ret = genericFunc(pVoid);
    return ret;
}

 

 

void 포인터의 사용법

void 포인터는 어떤 데이터 타입의 포인터도 담을 수 있다는 강점이 있습니다.

그렇기 때문에 다양한 방식의 사용법이 있습니다.

 

메모리 주소 출력

void 포인터는 데이터 타입에 상관없이, 메모리 주소를 가리키기 때문에 cout을 통해 객체의 주소를 출력할 수 있습니다.

int add(int a, int b){
    return a+b;
}

int main(){

    int (*pFunc)(int, int) = add;	// 함수 포인터
        
    cout << "Funtion Pointer adress: " << pFunc << "\n";

    cout << "void Pointer adress: " << (void*)pFunc;	// void 포인터로 타입 변환
    
    return 0;
}

함수명은 함수의 주소를 가리키고 있기 때문에, 함수의 주소가 출력될 것으로 예상되지만 1이 출력됩니다.

이것은 cout객체의 << 연산자를 overloading 할 때, 함수 포인터가 bool 타입으로 변경되기 때문입니다.

그렇지만, 함수 포인터를 void 포인터로 변경하면 이 문제를 해결할 수 있습니다.

 

메모리 동적 할당

malloc 함수는 메모리 할당하고 메모리의 주소를 void 포인터로 반환합니다.

또한, free 함수는 malloc 함수가 할당한 메모리를 해제합니다. 이때의 인자는 malloc 함수가 리턴한 void 포인터입니다.

#include <malloc.h>

void* malloc(size_t _Size);
void free(void *_Memory);

 

하지만, 할당한 메모리를 void 포인터를 통해서는 바로 사용할 수 없다는 점을 기억해야 합니다.

#define ITEM_NUM    100

int main(){

    void* pVoid = malloc(sizeof(int) * ITEM_NUM);
	
    int* pInt = static_cast<int*>(pVoid);	// int 포인터로 타입 변경
    
    for( int i = 0; i < ITEM_NUM; i++){
        pInt[i] = i;
    }
   
    free(pVoid);	// 메모리 해제
    
    return 0;
}

 

콜백(Callback) 함수

함수의 인자로 void 포인터를 사용하면 다양한 데이터 타입에 반응할 수 있는 유연하고 일반적인 함수를 만들 수 있습니다.

#include <iostream>
using namespace std;

void PrintIntData(void* pData){
    int* pInt = (int*)pData;
    cout << *pInt;
}

void PrintDoubleData(void* pData){
    double* pDouble = (double*)pData;
    cout << *pDouble;
}

void PrintData( void* pData, void (*pCallback)(void*) ){

    pCallback(pData);

    cout << " was printed.\n";	// 공통으로 출력되어야 할 내용
}

int main(){

    int a = 10;
    double d = 3.1415;

    PrintData(&a, PrintIntData);    // 데이터 타입에 따라 다른 반응을 할 수 있습니다

    PrintData(&d, PrintDoubleData);
    
    return 0;
}

void 포인터를 사용함으로써 PrintData 함수에 수정을 가하지 않고도 다양한 타입을 처리할 수 있습니다. 또한 PrintData는 데이터 타입별 출력 내용에 관여할 필요 없이 공통적인 내용에만 집중할 수 있을 겁니다.

 

함수에 함수를 전달하는 방법인 함수 포인터에 관한 설명은 아래 링크에서 보실 수 있습니다.

https://codingembers.co.kr/22

 

[C++] 함수 포인터 개념 및 사용하는 이유

함수 포인터란 무엇인가?이전 글에서 포인터를 이렇게 설명했습니다. 포인터란 객체의 메모리 주소를 저장하는 변수입니다.여기서 객체란 변수, 배열, 구조체, 클래스, 함수 등 메모리에

codingembers.co.kr

 

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유