C 프로그래밍에서 void 포인터의 개념


129

C 프로그래밍 언어에서 타입 캐스팅없이 void 포인터를 역 참조 할 수 있습니까?

또한 포인터를 받아서 void 포인터에 저장할 수있는 함수를 일반화하는 방법이 있습니까? 그리고 그 void 포인터를 사용하여 일반화 된 함수를 만들 수 있습니까?

예를 들어 :

void abc(void *a, int b)
{
   if(b==1)
      printf("%d",*(int*)a);     // If integer pointer is received
   else if(b==2)
      printf("%c",*(char*)a);     // If character pointer is received
   else if(b==3)
      printf("%f",*(float*)a);     // If float pointer is received
}

if-else 문을 사용하지 않고이 함수를 일반화하고 싶습니다. 가능합니까?

또한 void 포인터의 개념을 설명하는 훌륭한 인터넷 기사가 있다면 URL을 제공 할 수 있다면 유익 할 것입니다.

또한 void 포인터를 사용한 포인터 산술이 가능합니까?

답변:


97

C 프로그래밍 언어에서 타입 캐스팅을하지 않고 void 포인터를 역 참조 할 수 있습니까?

아니요, void유형이 없음을 나타내며 참조하거나 할당 할 수있는 것이 아닙니다.

포인터를 받아서 void 포인터에 저장할 수있는 함수를 일반화하는 방법이 있습니까? 그리고 그 void 포인터를 사용하여 일반화 된 함수를 만들 수 있습니다.

제대로 정렬되지 않았기 때문에 이식 가능한 방식으로 역 참조 할 수 없습니다. 데이터 유형에 대한 포인터는 데이터 유형 크기의 경계에 정렬되어야하는 ARM과 같은 일부 아키텍처에서는 문제가 될 수 있습니다 (예 : 32 비트 정수에 대한 포인터는 역 참조하려면 4 바이트 경계에 정렬되어야 함).

예를 들어, 읽기 uint16_t에서 void*:

/* may receive wrong value if ptr is not 2-byte aligned */
uint16_t value = *(uint16_t*)ptr;
/* portable way of reading a little-endian value */
uint16_t value = *(uint8_t*)ptr
                | ((*((uint8_t*)ptr+1))<<8);

또한 void 포인터로 포인터를 계산할 수 있습니다 ...

포인터 void아래에 구체적인 값이 없기 때문에 포인터에서 포인터를 산술 할 수 없으므로 크기가 다릅니다.

void* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */

2
내가 잘못 기억하지 않으면 두 가지 방법으로 void *를 안전하게 역 참조 할 수 있습니다. char *로 캐스트하는 것은 항상 허용되며, 원래 유형을 알고 있으면 해당 유형으로 캐스트 할 수 있습니다. void *는 타입 정보를 잃어 버렸으므로 다른 곳에 저장해야합니다.
Dan Olson

1
예, 다른 유형으로 캐스트하면 언제든지 역 참조 할 수 있지만 캐스트 하지 않으면 동일하게 수행 할 수 없습니다 . 간단히 말해, 컴파일러는 캐스팅 할 때까지 수학 연산에 사용할 어셈블리 명령어를 알 수 없습니다 .
Zachary Hamm

20
GCC는와 void *같은 포인터의 산술을 처리한다고 생각 char *하지만 표준이 아니므로 의존해서는 안됩니다.
ephemient 2016 년

5
잘 모르겠습니다. 예를 들어 수신 유형 위에 나열된 OP는 기능 사용자가 올바른 것으로 보장합니다. 알려진 유형의 값을 포인터에 할당 할 무언가가있는 경우이를 void 포인터로 캐스트 한 다음 정렬되지 않을 수있는 원래 포인터 유형으로 캐스트합니다. 나는 왜 컴파일러가 void 포인터로 캐스팅하기 때문에 무작위로 정렬하지 않는 것을 좋아하는지 이해하지 못합니다. 아니면 단순히 함수에 전달 된 인수의 유형을 사용자가 알지 못한다고 말하고 있습니까?
doliver

2
@ doliver # 2를 의미했습니다 ( "우리는 사용자가 함수에 전달한 인수의 유형을 알 수 없음"). 그러나 6 년 전 (내가 그토록 오래 걸렸습니까?)에서 내 대답을 다시 방문하면 의미가 무엇인지 알 수 있습니다. 항상 그런 것은 아닙니다 . 원래 포인터가 올바른 유형을 가리킬 때 이미 정렬되어 있으므로 정렬 문제없이 캐스팅하는 것이 안전합니다.
Alex B

31

C에서 a void *는 명시 적 캐스트없이 다른 유형의 객체에 대한 포인터로 변환 될 수 있습니다.

void abc(void *a, int b)
{
    int *test = a;
    /* ... */

그래도 더 일반적인 방식으로 함수를 작성하는 데 도움이되지 않습니다.

void *포인터를 역 참조하면 지정된 객체의 값을 가져 오기 때문에 a를 다른 포인터 유형으로 변환하여 a 를 역 참조 할 수 없습니다 . 나체 void는 유효한 유형이 아니므로 a void *를 역 참조 할 수 없습니다.

포인터 산술은 sizeof지정된 객체의 배수로 포인터 값을 변경하는 것 입니다. 다시 말하지만, void진정한 유형이 아니기 때문에 sizeof(void)의미가 없으므로 포인터 산술이에서 유효하지 않습니다 void *. (일부 구현에서는에 해당하는 포인터 산술을 사용하여 허용합니다 char *.)


1
@CharlesBailey 코드에서을 작성 void abc(void *a, int b)합니다. 그러나 void abc(int *a, int b)함수를 사용하면 결국 정의 int *test = a;할 것이기 때문에 하나의 변수를 저장하므로 다른 변수를 정의하여 다른 이점을 보지 못합니다. void *매개 변수로 사용 하고 나중에 함수에서 변수를 캐스팅 하여 이러한 방식으로 작성된 많은 코드를 봅니다 . 그러나이 기능은 작성자가 작성했기 때문에 변수의 사용법을 알아야하므로 왜 사용 void *합니까? 감사합니다
Lion Lai

15

C에서는 Java 또는 C #과 달리 void*포인터가 가리키는 객체 유형을 "추측"할 가능성이 전혀 없습니다 . getClass()이 정보를 찾을 수 없기 때문에 단순히 유사한 것이 존재하지 않습니다. 이러한 이유로, 당신이 찾고있는 "일반"의 종류는 항상 int b당신의 예제 나 printf함수 계열의 형식 문자열 과 같은 명시적인 메타 정보와 함께 제공됩니다 .


7

void 포인터는 일반 포인터로 알려져 있으며 모든 데이터 유형의 변수를 나타낼 수 있습니다.


6

지금까지 void 포인터에 대한 내 주장은 다음과 같습니다.

키워드 void를 사용하여 포인터 변수를 선언하면 범용 포인터 변수가됩니다. 모든 데이터 유형의 변수 (char, int, float 등)의 주소는 void 포인터 변수에 할당 될 수 있습니다.

main()
{
    int *p;

    void *vp;

    vp=p;
} 

다른 데이터 유형 포인터는 void 포인터에 할당 될 수 있으므로 absolut_value (아래 표시된 코드) 함수에서 사용했습니다. 일반적인 기능을 수행합니다.

정수 또는 부동 소수점을 인수로 사용하고 음수이면 + ve로 만드는 간단한 C 코드를 작성하려고했습니다. 다음 코드를 작성했습니다.

#include<stdio.h>

void absolute_value ( void *j) // works if used float, obviously it must work but thats not my interest here.
{
    if ( *j < 0 )
        *j = *j * (-1);

}

int main()
{
    int i = 40;
    float f = -40;
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    absolute_value(&i);
    absolute_value(&f);
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    return 0;
}   

그러나 오류가 발생하여 void 포인터에 대한 나의 이해가 올바르지 않다는 것을 알게되었습니다. (. 이제는 왜 그런지에 대한 포인트를 수집하려고합니다.

void 포인터에 대해 더 많이 이해해야하는 것은 그 것입니다.

void 포인터 변수를 역 참조하려면 형변환해야합니다. void 포인터에는 연관된 데이터 유형이 없기 때문입니다. 컴파일러가 void 포인터가 가리키는 데이터 유형을 알거나 추측 할 수있는 방법은 없습니다. 따라서 void 포인터가 가리키는 데이터를 가져 오기 위해 void 포인터 위치 내에 보유 된 올바른 유형의 데이터로 데이터를 타입 캐스트했습니다.

void main()

{

    int a=10;

    float b=35.75;

    void *ptr; // Declaring a void pointer

    ptr=&a; // Assigning address of integer to void pointer.

    printf("The value of integer variable is= %d",*( (int*) ptr) );// (int*)ptr - is used for type casting. Where as *((int*)ptr) dereferences the typecasted void pointer variable.

    ptr=&b; // Assigning address of float to void pointer.

    printf("The value of float variable is= %f",*( (float*) ptr) );

}

프로그래머가 최종 사용자가 입력 한 데이터 유형에 대해 확신하지 못하는 경우 void 포인터가 실제로 유용 할 수 있습니다. 이러한 경우 프로그래머는 빈 포인터를 사용하여 알 수없는 데이터 유형의 위치를 ​​가리킬 수 있습니다. 프로그램은 사용자에게 데이터의 유형을 알려주도록하는 방식으로 설정 될 수 있으며, 유형 캐스팅은 사용자가 입력 한 정보에 따라 수행 될 수있다. 코드 스 니펫은 다음과 같습니다.

void funct(void *a, int z)
{
    if(z==1)
    printf("%d",*(int*)a); // If user inputs 1, then he means the data is an integer and type casting is done accordingly.
    else if(z==2)
    printf("%c",*(char*)a); // Typecasting for character pointer.
    else if(z==3)
    printf("%f",*(float*)a); // Typecasting for float pointer
}

void 포인터에 대해 명심해야 할 또 다른 중요한 점은 – void 포인터에서는 포인터 산술을 수행 할 수 없다는 것입니다.

void *ptr;

int a;

ptr=&a;

ptr++; // This statement is invalid and will result in an error because 'ptr' is a void pointer variable.

그래서 나는 나의 실수가 무엇인지 이해했다. 나는 똑같이 고치고있다.

참고 문헌 :

http://www.antoarts.com/void-pointers-in-c/

http://www.circuitstoday.com/void-pointers-in-c .

새 코드는 아래와 같습니다.


#include<stdio.h>
#define INT 1
#define FLOAT 2

void absolute_value ( void *j, int *n)
{
    if ( *n == INT) {
        if ( *((int*)j) < 0 )
            *((int*)j) = *((int*)j) * (-1);
    }
    if ( *n == FLOAT ) {
        if ( *((float*)j) < 0 )
            *((float*)j) = *((float*)j) * (-1);
    }
}


int main()
{
    int i = 0,n=0;
    float f = 0;
    printf("Press 1 to enter integer or 2 got float then enter the value to get absolute value\n");
    scanf("%d",&n);
    printf("\n");
    if( n == 1) {
        scanf("%d",&i);
        printf("value entered before absolute function exec = %d \n",i);
        absolute_value(&i,&n);
        printf("value entered after absolute function exec = %d \n",i);
    }
    if( n == 2) {
        scanf("%f",&f);
        printf("value entered before absolute function exec = %f \n",f);
        absolute_value(&f,&n);
        printf("value entered after absolute function exec = %f \n",f);
    }
    else
    printf("unknown entry try again\n");
    return 0;
}   

감사합니다,


2

아니요, 불가능합니다. 역 참조 된 값의 유형은 무엇입니까?


2
void abc(void *a, int b) {
  char *format[] = {"%d", "%c", "%f"};
  printf(format[b-1], a);
}

이 프로그램이 가능합니까? ... C 프로그래밍에서 가능하다면 확인하십시오 ...
AGeek

2
예, 가능합니다 (b의 범위를 확인하지 않으면 코드가 위험하지만). Printf는 가변 개수의 인수를 취하고 a는 (포맷 정보없이) 포인터로 스택에 푸시되고 형식 문자열의 정보를 사용하여 va_arg 매크로를 사용하여 printf 함수 내부에 팝됩니다.
hlovdal 2009

@SiegeX : 이식성이없는 것과 정의되지 않은 것을 설명하십시오.
Gauthier

형식 지정자를 포함하는 변수를 전달하는 @Gauthier는 이식성이 없으며 잠재적으로 정의되지 않은 동작입니다. 당신이 그런 것을하고 싶다면 'vs'향미를보십시오 printf. 이 답변 에는 그에 대한 좋은 예가 있습니다.
SiegeX

실제로는 아마도 int b열거 형으로 바꿀 것이지만 나중에 해당 열거 형을 변경하면이 기능이 중단됩니다.
잭 스타우트

1

ifs를 사용하지 않고이 기능을 일반화하고 싶습니다. 가능합니까?

내가 볼 수있는 유일한 간단한 방법은 C 프로그래밍 언어 AFAIK에서 사용할 수없는 오버로드를 사용하는 것입니다.

프로그램에 대한 C ++ 프로그래밍 언어를 고려 했습니까? 또는 사용을 금지하는 제약이 있습니까?


좋아, 부끄러운 일이야. 내 관점에서 볼 때 해결책이 없습니다. 실제로 printf () & cout : 인쇄를 구현하는 두 가지 방법과 동일합니다. printf () if () 문을 사용하여 형식 문자열을 해독하십시오 (나는 가정 또는 비슷한 것). cout 경우 연산자 <<는 오버로드 된 함수
yves Baumes

1

다음은 포인터에 void대한 간단한 포인터입니다 . https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 — 무효 포인터

void 포인터는 어떤 유형의 객체를 가리키는 지 알 수 없으므로 직접 역 참조 할 수 없습니다! 대신, void 포인터는 먼저 역 참조되기 전에 다른 포인터 유형으로 명시 적으로 캐스트되어야합니다.

void 포인터가 무엇을 가리키는 지 모른다면 어떻게 캐스트 할 것인지 어떻게 알 수 있습니까? 궁극적으로, 그것은 추적하는 당신에게 달려 있습니다.

무효 포인터 기타

void 포인터에서는 포인터 산술을 수행 할 수 없습니다. 포인터를 산술하려면 포인터가 가리키는 크기 개체를 알아야 포인터를 적절하게 늘리거나 줄일 수 있기 때문입니다.

머신의 메모리가 바이트 주소 지정 가능하고 정렬 된 액세스가 필요하지 않다고 가정하면, a를 해석하는 가장 일반적이고 원자적인 (머신 레벨 표현에 가장 가까운) 방법은 void*바이트에 대한 포인터 uint8_t*입니다. 예를 들어 a void*를 캐스팅하면 uint8_t*해당 주소에서 시작하는 첫 번째 1 / 2 / 4 / 8 / 다수의 바이트를 인쇄 할 수 있지만 다른 작업은 수행 할 수 없습니다.

uint8_t* byte_p = (uint8_t*)p;
for (uint8_t* i = byte_p; i < byte_p + 8; i++) {
  printf("%x ",*i);
}

0

빈 프린터를 쉽게 인쇄 할 수 있습니다

int p=15;
void *q;
q=&p;
printf("%d",*((int*)q));

1
누가 void같은 크기를 보장 int합니까?
Ant_222

이것은 기술적으로 void포인터를 인쇄하는 것이 아니라 int포인터에 포함 된 메모리 주소에 인쇄합니다 (이 경우 값 p).
Marionumber1

0

C는 정적으로 형식화 된 강력한 언어이므로 컴파일하기 전에 변수 유형을 결정해야합니다. C에서 제네릭을 에뮬레이션하려고하면 C ++을 다시 작성하려고 시도하므로 C ++을 대신 사용하는 것이 좋습니다.


0

void 포인터는 일반 포인터입니다. 변수의 모든 데이터 유형의 주소는 void 포인터에 할당 될 수 있습니다.

int a = 10;
float b = 3.14;
void *ptr;
ptr = &a;
printf( "data is %d " , *((int *)ptr)); 
//(int *)ptr used for typecasting dereferencing as int
ptr = &b;
printf( "data is %f " , *((float *)ptr));
//(float *)ptr used for typecasting dereferencing as float

0

다른 데이터 유형은 메모리에서 크기가 다르므로 (예 : int는 4 바이트, char은 1 바이트) 포인터 유형을 지정하지 않으면 포인터를 역 참조 할 수 없습니다.


-1

기본적으로 C에서 "유형"은 메모리의 바이트를 해석하는 방법입니다. 예를 들어 다음 코드는

struct Point {
  int x;
  int y;
};

int main() {
  struct Point p;
  p.x = 0;
  p.y = 0;
}

"main을 실행할 때 4 (정수 크기) + 4 (정수 크기) = 8 (총 바이트)의 메모리를 할당하려고합니다. 유형 레이블이있는 값에 '.x'를 lvalue로 쓸 때 컴파일 타임을 가리키고 포인터의 메모리 위치에서 4 바이트를 더한 데이터를 검색합니다. 반환 값에 컴파일 타임 레이블 "int"를 지정합니다.

런타임에 컴퓨터 내부에서 "포인트"구조는 다음과 같습니다.

00000000 00000000 00000000 00000000 00000000 00000000 00000000

그리고 void*데이터 유형은 다음과 같습니다. (32 비트 컴퓨터 가정)

10001010 11111001 00010010 11000101

이것은 질문에 대답하지 않습니다
MM

-2

이것은 작동하지 않지만 void *는 함수에 대한 일반적인 포인터를 정의하고 다른 함수 (Java의 콜백과 비슷한)에 대한 인수로 전달하는 데 많은 도움이되거나 oop과 유사한 구조를 정의하는 데 도움이 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.