C编程中void指针的概念

Posted

技术标签:

【中文标题】C编程中void指针的概念【英文标题】:Concept of void pointer in C programming 【发布时间】:2010-10-16 02:34:44 【问题描述】:

在 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 语句的情况下使这个函数通用 - 这可能吗?

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

另外,是否可以使用 void 指针进行指针运算?

【问题讨论】:

对于某些人来说,如果 * 附加到“类型”而不是名称,这可能会更好读。 IE。 a 是指向某个 void(内存漏洞)的指针。因此,在每个打印语句中,我们告诉编译器我们期望a 指向哪种“空洞/空洞”,然后我们从该指针中获取那种值。 (没有研究 LLP32/LP64 困难的一些 size_t 问题;-) 【参考方案1】:

在 C 编程语言中是否可以在不进行类型转换的情况下取消引用 void 指针...

不,void 表示没有类型,它不是你可以取消引用或分配的东西。

有什么方法可以泛化一个函数,它可以接收指针并将其存储在 void 指针中,通过使用该 void 指针,我们可以创建一个泛化函数..

您不能只是以可移植的方式取消引用它,因为它可能没有正确对齐。这可能是一些架构(如 ARM)的问题,其中指向数据类型的指针必须在数据类型大小的边界处对齐(例如,指向 32 位整数的指针必须在 4 字节边界处对齐才能取消引用)。

例如,从void*读取uint16_t

/* 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?? */

【讨论】:

除非我记错了,否则您可以通过两种方式安全地取消引用 void*。转换为 char* 始终是可以接受的,如果您知道它指向的原始类型,则可以转换为该类型。 void* 丢失了类型信息,因此必须将其存储在其他地方。 是的,如果你转换成另一种类型,你总是可以取消引用,但如果你转换,你就不能这样做。简而言之,编译器不会知道将哪些汇编指令用于数学运算直到你转换它。 我认为 GCC 对 void * 指针的算术处理与 char * 相同,但它不是标准的,你不应该依赖它。 我不确定我是否遵循。例如,函数用户保证上面列出的 OP 传入类型是正确的。你是说如果我们有一些东西,我们将一个已知类型的值分配给一个指针,将它转换为一个 void 指针,然后将它转换回它可能变得未对齐的原始指针类型?我不明白为什么编译器会像这样破坏你,只是因为你将它们强制转换为一个 void 指针。或者你只是说我们不能相信用户知道他们传递给函数的参数类型? @doliver 我的意思是#2(“我们不能相信用户知道他们传递给函数的参数类型”)。但是重新审视我 6 年前的回答(呃,真的有那么长吗?)我明白你的意思,它并非总是:当原始指针指向正确的类型时,它将已经对齐,因此可以安全地投射而不会出现对齐问题。【参考方案2】:

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

void abc(void *a, int b)

    int *test = a;
    /* ... */

不过,这无助于以更通用的方式编写函数。

您不能通过将void * 转换为不同的指针类型来取消引用它,因为取消引用指针正在获取指向对象的值。裸露的 void 不是有效类型,因此无法取消引用 void *

指针算法是关于将指针值更改为sizeof 指向对象的倍数。同样,因为void 不是真正的类型,sizeof(void) 没有意义,所以指针算术在void * 上无效。 (一些实现允许它,使用 char * 的等效指针算法。)

【讨论】:

@CharlesBailey 在你的代码中,你写了void abc(void *a, int b)。但是为什么不使用void abc(int *a, int b),因为在你的函数中你最终会定义int *test = a;,它会保存一个变量,而且我没有看到定义另一个变量的任何其他优势。我看到许多以这种方式编写的代码使用void * 作为参数,并在函数的后面转换变量。但是既然这个函数是作者写的,所以他必须知道变量的用法,所以为什么要用void *呢?谢谢 我相信你想说的是“你不能取消引用 void * 没有将其转换为不同的指针类型”【参考方案3】:

您应该知道,在 C 中,与 Java 或 C# 不同,绝对不可能成功“猜测”void* 指针指向的对象类型。类似于getClass() 的东西根本不存在,因为找不到此信息。出于这个原因,您正在寻找的那种“通用”总是带有明确的元信息,例如您的示例中的 int bprintf 系列函数中的格式字符串。

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

到目前为止,我对 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 指针指向的数据,我们使用 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 指针来指向未知数据类型的位置。程序可以设置为要求用户告知数据的类型,并且可以根据用户输入的信息进行类型转换。下面给出一个代码sn-p。

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;
   

谢谢,

【讨论】:

【参考方案6】:

不,这是不可能的。解引用的值应该是什么类型?

【讨论】:

【参考方案7】:
void abc(void *a, int b) 
  char *format[] = "%d", "%c", "%f";
  printf(format[b-1], a);

【讨论】:

这个程序是否可行......请检查一下,如果这在 C 编程中可行...... 是的,这是可能的(尽管如果没有对 b 进行范围检查,代码很危险)。 Printf 接受可变数量的参数,并且 a 只是作为任何指针(没有任何类型信息)被压入堆栈,并使用格式字符串中的信息使用 va_arg 宏在 printf 函数中弹出。 @SiegeX:请描述什么是不可移植的,什么是未定义的。 @Gauthier 传递包含格式说明符的变量是不可移植的并且可能是未定义的行为。如果您想做类似的事情,请查看printf 的“vs”风格。 This answer 有一个很好的例子。 在实践中,我可能会用枚举替换int b,但以后对该枚举进行任何更改都会破坏此功能。【参考方案8】:

这里有一个关于void指针的简短指针:https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 — 空指针

因为void指针不知道自己指向的是什么类型的对象,所以不能直接解引用!相反,void 指针必须先显式转换为另一种指针类型,然后才能取消引用。

如果一个 void 指针不知道它指向什么,我们怎么知道要把它转换成什么?最终,这取决于您来跟踪。

空指针杂项

不可能对 void 指针进行指针运算。这是因为指针运算需要指针知道它所指向的对象大小,所以它可以适当地增加或减少指针。

假设机器的内存是字节可寻址的并且不需要对齐访问,解释void* 的最通用和原子(最接近机器级表示)的方法是作为一个字节的指针,@ 987654324@。例如,将 void* 转换为 uint8_t* 将允许您打印出从该地址开始的前 1/2/4/8/however-many-you-desire 字节,但您不能做太多否则。

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

【讨论】:

【参考方案9】:

我想让这个函数通用, 不使用 ifs;有可能吗?

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

您是否为您的程序考虑过 C++ 编程语言?还是有什么限制禁止使用?

【讨论】:

好吧,真可惜。从我的角度来看,我没有看到任何解决方案。实际上,它与 printf() & cout 相同:2 种不同的方式来实现打印。 printf() 使用 if() 语句来解码格式字符串(我想或类似的东西),而对于 cout 案例运算符 【参考方案10】:

空指针是没有与之关联的数据类型的指针。空指针可以保存任何类型的地址,并且可以被类型转换为任何类型。但是,void指针不能直接解引用。

int x = 1;
void *p1;
p1 = &x;
cout << *p1 << endl; // this will give error
cout << (int *)(*p) << endl; // this is valid

【讨论】:

【参考方案11】:

您可以轻松打印空打印机

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

【讨论】:

谁保证voidint的大小一样? 这在技术上不是打印void 指针,而是在指针包含的内存地址处打印int(在本例中为p 的值)。跨度> 【参考方案12】:

因为 C 是静态类型的强类型语言,所以必须在编译前确定变量的类型。当您尝试在 C 中模拟泛型时,您最终会尝试再次重写 C++,因此最好使用 C++。

【讨论】:

【参考方案13】:

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

【讨论】:

【参考方案14】:

您不能在不指定类型的情况下取消引用指针,因为不同的数据类型在内存中具有不同的大小,即 int 为 4 个字节,char 为 1 个字节。

【讨论】:

【参考方案15】:

从根本上说,在 C 中,“类型”是一种解释内存中字节的方式。比如下面的代码是什么

struct Point 
  int x;
  int y;
;

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

说“当我运行 main 时,我想分配 4(整数大小)+ 4(整数大小)= 8(总字节)的内存。当我将 '.x' 作为左值写入一个值时类型标签指向编译时,从指针的内存位置加上四个字节检索数据。将返回值指定为编译时标签“int”。

在计算机运行时,您的“点”结构如下所示:

00000000 00000000 00000000 00000000 00000000 00000000 00000000

您的void* 数据类型可能如下所示:(假设是 32 位计算机)

10001010 11111001 00010010 11000101

【讨论】:

这不能回答问题【参考方案16】:

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

【讨论】:

以上是关于C编程中void指针的概念的主要内容,如果未能解决你的问题,请参考以下文章

C++编程知识:什么是万能指针?详解C语言万能指针的妙用

指针中容易混淆的概念以及常见笔试题

void 型指针的高阶用法,你掌握了吗?

C中void指针的指针算法

c# 调用c dll void 指针类型转化问题

C语言空指针NULL以及void指针