본문 바로가기

C언어

Chapter 14. void형 포인터

Chapter 14.void형 포인터

널 포인트는 아무것도 가리키지 않는 포인터였다.

void형 포인트는 그와 반대로어느 것이든 가리킬 수 있는 포인터이다.

좀더 자세히말하면void형 포인터는 현재 가리키고 있는 대상체가 정해져 있지않은 포인터가 된다.

void *imsip;

imsip는가리키는 대상체가 정해져 있지 않은 포인터 변수라고 정의했다.

따라서,imsip 안에는 분명 주소 값이 저장되어 있을 것이다. 그렇다면, 현재 가리키고 있는 주소 값에서 몇 byte를 읽어올 것인가에 대한 질문이 된다.

imsip 자체는 가리키는 대상체가 따로 지정? 있지 않기 때문에 몇byte를 읽어올 지 알 수가 없다.

void형포인터변수가 가리키는 주소에서 몇byte를 읽어올 지 결정하는 것은캐스트 연산자이다.

void형 변수 포인터 정의

void *imsip

앞서 말한것처럼변수명 앞에 "void *"만 붙여주면 된다.

void형 포인터 변수의 성질가 사용

1. 어떠한 형 변환(캐스트 연산자 사용) 없이도void형 포인터 변수에 대입이 가능하다.

14_1.c

#include <stdio.h>

main()
{
int imsi_int; // or int *imsi_int;
float imsi_float; // or float*imsi_float;
double imsi_double; // or double*imsi_double;
char imsi_char;// or char *imsi_char

void *imsip;

imsip = &imsi_int; //or imsip = imsi_int;
imsip = &imsi_float;//or imsip = imsi_float;
imsip = &imsi_double;//or imsip = imsi_double;
imsip = &imsi_char;//or imsip = imsi_char;
}

gcc -o 14_1 14_1.c

위에서 보이는 것처럼 imsip에 할당된는 것이 주소 값이면 어떠한 형(type)이든지 상관없다.

2. void형 포인터 변수에서 값을 읽을 때는반드시 캐스트 연산자를 사용해야 한다.

14_3.c

#include <stdio.h>

main()
{
int imsi_int = 5;
float imsi_float = 5.5;
double imsi_double = 0.7;
char imsi_char = 'K';

void *imsip;

imsip = &imsi_int;
printf("imsi_int : %d\n", *(int *)imsip);

imsip = &imsi_float;
printf("imsi_float : %.1f\n", *(float *)imsip);

imsip = &imsi_double;
printf("imsi_double : %.1f\n", *(double *)imsip);

imsip = &imsi_char;
printf("imsi_char : %c\n", *(char *)imsip);
}

gcc -o 14_3 14_3.c
./14_3
imsi_int : 5
imsi_float : 5.5
imsi_double : 0.7
imsi_char : K

void형 포인터 변수가 가리키는 주소에서 몇 바이트를 읽어올 지 정해지지 않은 상태이므로 이를 해결하기 위해캐스트 연산자를 이용하고 있다.

printf를 이용할때(int *)imsip를사용하면 안된다.imsip에 들어 있는 값을주소화 하라는 의미 밖에 없으니 말이다.

조심할 것은int형의 주소 값을 imsip에 저장한 후float형이나 double형으로 꺼내온다든지 하는 일을 수행하면 당연히 문제를 발생 시킨다.

printf("imsi_int : %f\n", *(double *)imsip);

imsi_int : -0.274408

3. '*'연산자(간접지정 연산자)를 사용할 때는 항상 캐스트 연산자를사용한다.

14_4.c

#include <stdio.h>

main()
{
int imsi_int;
void *imsip;

imsip = &imsi_int;

printf("%d\n", *imsip);
}

gcc -o 14_4 14_4.c
14_4.c: In function 'main'=>
14_4.c: 9: warning: dereferencing 'void *' pointer
14_4.c: 9: error: invalid use of void expression

imsip가 가리키는 곳에서 몇 byte를 읽어 올지는 형(type)이 결정하는데 위 프로그램에선 알 수가 없다.

*imsip라고사용하면imsip가 가리키는 곳에서 4byte를 읽어 이를 사용자에게 알리지 못한다.

따라서, 다음과 같이 캐스트 연산자를 이용하여 imsip의 값을 int형으로 주소화 하고 이ㅡㄹ 참조항 하나의 값을 취한 후 그 값을 출력하도록 해야한다.

printf("%d\n", *(int *)imsip);

4. void형 포인ㅌ 변수에 ++, --를 사용할때에는 항상 캐스트 연산자를 사용한다.

++나 --를 사용하려면 형(type)을 알아야 그 형(type)만큼의 분기를 할 수가 있는데 모르면 몇 byte식을 분기해야 하는지 컴파일러는 알 수 없기 때문에 에러가 발생한다.

1~4번을 정리하는 의미로다음 예제를 통해 좀더 익숙해지자.

14_5.c

#include <stdio.h>

main()
{
char *stringp, STRING[] = "archie";
int *intp, INT[] = {7, 4, 9};
void *voidp;
stringp = STRING;
intp = INT;

printf("stringp : %#010x\n", stringp);
printf("intp : %#010x\n", intp);

puts("-------------------------");

printf("stringp : %s\n", stringp);
printf("STRING : %s\n", STRING);
puts("-------------------------");

printf("INT : %d %d %d\n", INT[0], INT[1], INT[2]);
printf("intp : %d %d %d\n", *intp, *(intp+1), *(intp+2));

puts("-------------------------");

voidp = stringp; // voidp = STRING;
printf("(char *) : %s\n", (char *)voidp);
printf("(char *)voidp : %#010x\n", (char *)voidp);
printf("(char *)voidp : %c %c %c %c %c\n", *(char *)voidp,
*((char *)voidp+1),
*((char *)voidp+2),
*((char *)voidp+3),
*((char *)voidp+4));
puts("-------------------------");

voidp = intp; // voidp = INT;
printf("(int *)voidp : %d %d %d\n", *(int *)voidp,
*((int *)voidp+1),
*((int *)voidp+2));

puts("-------------------------");

voidp = STRING;
for(;*(char *)voidp;(char *)voidp++)
printf("*(char *)voidp : %c\n", *(char *)voidp);

puts("-------------------------");

voidp = INT;
{
int i;
for(i=0;i<sizeof(INT)/sizeof(int);i++)

//fedora core 9gcc 에서컴파일 에러 발생(error: invalid lvalue in increment)

//unix aix cc에서 테스트한 결과 정상 컴파일

printf("*(int *)voidp : %d\n", *((int *)voidp)++);
}
}

gcc -o14_5 14_5.c

./14_5

stringp : 0xbfa90d65
intp : 0xbfa90d58
-------------------------
stringp : archie
STRING : archie
-------------------------
INT : 7 4 9
intp : 7 4 9
-------------------------
(char *) : archie
(char *)voidp : 0xbfa90d65
(char *)voidp : a r c h i
-------------------------
(int *)voidp : 7 4 9
-------------------------
*(char *)voidp : a
*(char *)voidp : r
*(char *)voidp : c
*(char *)voidp : h
*(char *)voidp : i
*(char *)voidp : e
-------------------------
*(int *)voidp : 7
*(int *)voidp : 4
*(int *)voidp : 9

*((int *)voidp)++

printf("%d\n", *((int *)voidp)++);

prntf("%d\n", *(int *)voidp++);

*(int *)voidp++는*((int *)(voidp++))와같다.

*((int *)voidp)++ 수식을 하나씩 정리하면 다음과같다.

  • voidp
    void형 포인터변수. 어떠한 주소 값도 들어갈 수 있으며 단지가리키는 곳에서 몇바이트를 읽어올지 알 수 없으므로 캐스트 연산자를 이용한다.
  • (int *)voidp
    voidp에 있는 값을 int형 주소값으로 변환. voidp에서 값을 꺼내올 때 voidp가 가리키는 곳에서부터 4byte를 읽어 온다는 말이다.
  • ((int*)voidp)++
    voidp에 있는 값을 int형 주소 값으로 변환하고 ++를 이용하여 int형만큼 분기한다. ++는 후치 연산이기 때문에 다음에 이를 적용받게 된다.
  • *((int*)voidp)++
    ++에 걸리는 대상체가 괄호 전체가 되므로 ++의 대상체는 (int *)voidp가 된다.
    voidp에 들어 있는 값을int형을 가리키는 주소 값으로 변환했기때문에++수식을 사용할 수 있는 것이다.
    ++에 걸리는 대상체가 "int *"라는것을 다음 프로그램으로 확인할 수 있다.

14_6.c

#include <stdio.h>

main()
{
int imsi[3] = {6, 3, 7};
int *imsip;
void *voidp;

imsip = imsi;
voidp = imsip;

printf("1. %d\n", *((double *)voidp)++);
printf("2. %d\n", *(int *)voidp);
}

gcc-o 14_6 14_6.c

./14_6

1. 6
2. 7

*((double *)voidp)++에 의해서 증가되는 주소 값은 8byte가 된다.++의 대상체는 (double *)voidp이기때문이다.

8byte가 움직인 상태에서*(int)voidp를 출력해보면 당연히 3이 아닌 7이 출력될 것이다.

주의 사항

void형 포인터에서는 다음만 조심한단면 사용하는데 무리가 없을 것이다.

#include <stdio.h>

main()
{
void imsi(int *temp);
}
void imsi(int temp[])
{
...
}

적법한 프로그램이다.

#include <stdio.h>

main()
{
void imsi(int *temp);
}
void imsi(void temp[])
{
...
}

void형 포인터는 첨자 연산을 할 수 없기 때문에 위의 프로그램은 위법이다.

첨자 연산을 하려면 역시 캐스트 연산자가 필요한데, imsi()의 본체에서 이를 위한 어떠한 행위도 하고 있지 않기 때문이다.

'C언어' 카테고리의 다른 글

Chapter 17. 구조체와 포인터  (0) 2011.10.16
Chapter 13. (int *)pointer  (0) 2011.10.16
Chapter 10. 바로알자 getchar()  (0) 2011.10.16
Chapter 11. 가변 인자  (0) 2011.10.16
Chapter 12. 될 것 같으면서 안 되는 코드  (0) 2011.10.16