C언어는 정확한 데이터 타입을 요구합니다. 그런데, 가끔은 데이터 타입을 정하기 어렵거나 다양한 데이터 타입을 저장해야 하는 경우가 있습니다. 또는 내부적으로는 데이터 타입이 정해졌지만 외부에 노출하면 사용자들이 데이터를 수정하여 문제가 발생하는 경우에는 타입을 명확하게 표한하는 것이 좋지 않을 수 있습니다. 이와 같이 정해지지 않은 데이터 타입 또는 자료 구조를 비공개를 처리하고 싶은 경우에 사용할 수 있는 데이터 타입이 void *형입니다.

 

void *변수명;
void *변수명 = NULL;

 

void *에 대입할 수 있는 것은 모든 종류의 포인터 타입 데이터를 저장할 수 있습니다. 또한 Warning이 발생하더라도 무시한다면, char, short, int, long형의 데이터도 대입 가능합니다.

 


void *를 사용해야 하는 경우의 예입니다.

 

1. 불특정 데이터 처리

struct student
{
    int  no;
    char name[50];
    int  birth_year;
};

int compare_name(void *studen1, void *student2)
{
    struct student *s1 = (struct student *)student1;
    struct student *s2 = (struct student *)student2;
    
    return strcmp(s1->name, s2->name);
}

int compare_number(void *studen1, void *student2)
{
    struct student *s1 = (struct student *)student1;
    struct student *s2 = (struct student *)student2;
    
    return s1->no - s2->no;
}

qsort(3)함수의 경우 다양한 데이터를 sorting해야 하므로 sorting을 위한 함수의 파라미터는 정할 수 없으므로 함수의 soring함수의 type은

int compare(void *v1, void *v2);

의 형식으로 정의해야 합니다. 위의 경우에는 sorting을 위해서 데이터 타입을 특정할 수 없기 때문에 void *를 사용하였습니다. (물론, 더 중요한 것은 void *보다는 함수 포인터의 사용입니다.)

 


 

2. 비공개형 자료구조용

특정 솔루션을 개발을 하여 배포하는 경우에, 그 자료구조가 외부로 노출되면, 즉, 직접 데이터를 수정하면 문제가 되는경우가 있을 수 있습니다. 그 데이터는 제공하는 함수에서만 사용되고 수정되어야 하는 경우입니다.

 

#ifndef __SOLUTION_H__
#define __SOLUTION_H__

typedef void SOLUTION;

SOLUTION * sol_open(void);
int sol_read(SOLUTION *sol, char *data, int size);
int sol_write(SOLUTION *sol, const char *data, int size);
void sol_close(SOLUTION *);

#endif

header 퍄일은 위와 같이 void * 타입을 사용하는 함수로 선언합니다.

 

그런데, C source에서는 내부적으로 별도의 자료 구조를 정하여 사용합니다.

#include "solution.h"
#include <string.h>

typedef struct {
    int cur_position;
    .....
} _solution_;


SOLUTION *sol_open(void)
{
    _solution_ *tmp =(_solution_ *) malloc(sizeof(_solution_));
    ....
    
    return (void*)tmp;
}

int sol_read(SOLUTION *sol, char *data, int size)
{
    _solution_ *s = (_solution_ *)sol;
    
    ......
    
    s-.cur_position += size;
    
    ......
}

위와 같이 솔루션을 개발하면 자료구조가 공개되지 않아서 일반사용자는 데이터를 함부로 처리하지 못합니다. 또한 나중에 version up 등으로 인하여 자료구조가 변경되어도 사용자들에게는 영향을 주지 않고 손쉽게 처리할 수 있습니다.

 


void *의 연산

 

일반적인 포인터 연산은 그 데이터의 자료형에 따라서 메모리의 증감이 달라집니다.

 

char *ptr;

printf("ptr = %p\n", ptr);
printf("ptr + 4 = %p\n", ptr + 4);

위의 경우 ptr에 저장할 주소의 데이터 타입이 char 이므로 ptr + 4는 ptr의 메모리 번지보다 4가 큰 값을 가집니다.

 

double *ptr;

printf("ptr = %p\n", ptr);
printf("ptr + 4 = %p\n", ptr + 4);

의 경우는 double이 8바이트를 가지므로 ptr보다 ptr + 4는 8 * 4 = 32가 더 큰 메모리 번지를 가리킵니다. 이와 같이 포인터 변수의 데이터 타입에 따라서 증갑믜 크기가 결정됩니다.

 

 

double arr[200];
void *ptr = arr;

printf("ptr = %p\n", ptr);
printf("ptr + 4 = %p\n", ptr + 4);

위의 경우, void * 변수인 ptr에 double *를 대입시켰으므로 4 * 8 = 32가 증가하기를 바라겠지만, void *는 그렇지 않고 4 증가하므로 포인터 연산시에 주의해야 합니다.

double arr[100];
void *ptr = arr;

printf("ptr = %p\n", ptr);
printf("ptr + 4 = %p\n", ptr + sizeof(double) * 4);

처럼 데이터 크기를 sizeof( )한 결과 값을 곱해주어야 합니다.

 

 

 

 

C Programming Language 문법

1. C 프로그래밍 언어는? 2. C언어 개발 환경 (실습 환경) 3. C언어의 컴파일 과정 4. C 소스 파일 구성 5. 주석문(Comment) 6. 식별자 명명 규칙 7. C 프로그래밍의 시작 - 함수 8. 변수와 상수 (정수형) 9. 변..

www.it-note.kr

 

블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

Tag ,

댓글을 달아 주세요