C编程中void指针的概念


129

是否可以取消引用void指针而无需使用C编程语言进行类型转换?

另外,有没有一种方法可以泛化一个可以接收指针并将其存储在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语句-这可能吗?

另外,如果有很好的互联网文章解释了空指针的概念,那么提供URL将是有益的。

另外,使用空指针的指针算术是否可能?

Answers:


97

是否可以取消引用void指针而不用C编程语言进行类型转换...

否,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* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */

2
除非我记错了,否则可以通过两种方式安全地取消引用void *。强制转换为char *总是可以接受的,如果您知道原始类型,则可以转换为该类型。void *丢失了类型信息,因此必须将其存储在其他位置。
丹·奥尔森,2009年

1
是的,你可以随时提领,如果你投成另一种类型,但你不能做同样的,如果你投。简而言之,直到您强制转换,编译器才知道用于数学运算的汇编指令。
Zachary Hamm,2009年

20
我认为GCC对void *指针的算术处理与相同char *,但它不是标准的,您不应依赖它。
短暂

5
我不确定是否要遵循。例如,函数的用户可以保证输入类型上方列出的OP是正确的。您是在说如果我们在某处给指针分配了一个已知类型的值,然后将其强制转换为一个空指针,然后将其强制转换为可能不对齐的原始指针类型?我不明白为什么编译器仅仅因为将它们强制转换为void指针而破坏了您的工作,只是随机地将它们对齐。还是您只是在说我们不能信任用户知道他们传递给函数的参数类型?
doliver

2
@doliver我的意思是#2(“我们不能相信用户知道他们传递给函数的参数的类型”)。但是重新回顾我6年前的答案(嗯,真的这么久了吗?)我可以理解您的意思,但情况并非总是如此:当原始指针指向正确的类型时,它已经对齐,因此可以安全地进行投放,而不会出现对齐问题。
Alex B

31

在C语言中,void *可以将a转换为指向不同类型对象的指针,而无需显式强制转换:

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

但是,这不利于以更通用的方式编写函数。

您不能通过void *将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 *?谢谢
莱昂狮

15

您应该意识到,在C语言中,与Java或C#不同,绝对不可能成功地“猜测” void*指针指向的对象的类型。getClass()根本没有类似的东西存在,因为找不到此信息。因此,您要查找的“泛型”始终带有显式的元信息,int b例如示例中的或printf函数族中的格式字符串。


7

空指针称为通用指针,它可以引用任何数据类型的变量。


6

到目前为止,我对void指针的轻描淡写如下。

当使用关键字void声明指针变量时,它将成为通用指针变量。任何数据类型(char,int,float等)的任何变量的地址都可以分配给void指针变量。

main()
{
    int *p;

    void *vp;

    vp=p;
} 

由于可以将其他数据类型的指针分配给void指针,因此我在absolut_value(下面显示的代码)函数中使用了它。使具有一般功能

我试图编写一个简单的C代码,将整数或浮点数作为参数,并尝试使其为+ ve(如果为负)。我写了下面的代码,

#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 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 *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 2009年

2
是的,这是可能的(尽管如果不对b进行范围检查,代码很危险)。Printf接受可变数量的参数,并且a作为任何指针(没有任何类型信息)被压入堆栈,并使用va_arg宏(使用格式字符串中的信息)在printf函数内部弹出。
hlovdal

@SiegeX:请描述什么是不可移植的,以及哪些是不确定的。
Gauthier

@Gauthier传递包含格式说明符的变量不是可移植的,并且可能是未定义的行为。如果您想执行类似的操作,请查看的“ vs”风格printf这个答案就是一个很好的例子。
SiegeX

实际上,我可能会int b用一个枚举代替,但是以后对该枚举进行任何更改都会破坏此功能。
杰克·斯托特

1

我想使此函数通用,而不使用ifs; 可能吗?

我看到的唯一简单方法是使用重载..在C编程语言AFAIK中不可用。

您是否为您的程序考虑过C ++程序语言?还是有禁止使用它的约束?


好的,真可惜。从我的角度来看,那时没有解决方案。实际上,它与printf()和cout相同:实现打印的两种不同方式。printf()使用if()语句解码格式字符串(我想是类似的东西),而对于cout情况,运算符<<是一个重载函数
yves Baumes 09年

1

以下是有关void指针的简要说明:https : //www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 —空指针

因为void指针不知道其指向的对象类型,所以不能直接取消引用它!相反,必须先将void指针显式转换为另一种指针类型,然后再将其取消引用。

如果void指针不知道它指向的是什么,我们如何知道将其强制转换为什么?最终,这取决于您自己进行跟踪。

空指针杂项

无法对void指针执行指针算术运算。这是因为指针算术要求指针知道其指向的对象大小,因此它可以适当地递增或递减指针。

假设机器的内存是字节可寻址的,并且不需要对齐的访问,则解释a的最通用和原子的(最接近机器级别的表示形式)void*是指向a字节的指针uint8_t*。例如,void*将a 强制转换为a 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 '16

从技术上讲,这不是打印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'时指向编译时,从指针的内存位置中检索数据再加上四个字节。为返回值提供编译时标签“ int”。

运行时在计算机内部,“ Point”结构如下所示:

00000000 00000000 00000000 00000000 00000000 00000000 00000000

这是您的void*数据类型可能是什么样的:(假设是32位计算机)

10001010 11111001 00010010 11000101

这不回答这个问题
MM

-2

这是行不通的,但是void *可以在定义指向函数的通用指针并将其作为参数传递给另一个函数(类似于Java中的回调)或将其定义为类似于oop的结构方面大有帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.