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 b
或 printf
系列函数中的格式字符串。
【讨论】:
【参考方案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));
【讨论】:
谁保证void
和int
的大小一样?
这在技术上不是打印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指针的概念的主要内容,如果未能解决你的问题,请参考以下文章