当指向特定类型(例如 int
, char
, float
, ..) 的指针被递增时,它的值将增加该数据类型的大小。如果一个指向大小为 x
的数据的 void
指针被递增,它如何到达指向 x
字节之后的位置?编译器如何知道要将 x
添加到指针的值中?
当指向特定类型(例如 int
, char
, float
, ..) 的指针被递增时,它的值将增加该数据类型的大小。如果一个指向大小为 x
的数据的 void
指针被递增,它如何到达指向 x
字节之后的位置?编译器如何知道要将 x
添加到指针的值中?
最终结论:在C语言和C++中,对 void*
进行算术运算是不合法的。
GCC 作为扩展允许它,参见指针算术运算(注意,该部分是手册中“C扩展”章节的一部分)。 Clang 和 ICC 可能允许对 void*
进行算术运算以与GCC保持兼容。 其他编译器(如MSVC)不允许对 void*
进行算术运算,如果指定了 -pedantic-errors
标志或 -Werror=pointer-arith
标志,则GCC也不允许(如果您的代码库还必须使用 MSVC 编译,则此标志很有用)。
引用自 n1256 草案。
该标准对加法操作的描述如下:
6.5.6-2: 对于加法,要么两个操作数都属于算术类型, 要么一个操作数是指向对象类型的指针, 另一个操作数是整数类型。
因此,问题在于 void*
是否是指向“对象类型”的指针,
或等效地说,void
是否是“对象类型”。 “对象类型”的定义为:
6.2.5.1:类型分成对象类型(完全描述对象的类型), 函数类型(描述函数的类型)和不完整类型 (描述对象但缺少确定其大小所需的信息)。
标准将 void
定义为:
由于void是不完整类型,它不是对象类型,因此它不是加法运算的有效操作数。因此,您不能对void指针执行指针算术运算。6.2.5-19:
void
类型包括 一组空值; 它是无法完成的不完整类型。
void*
指针算术运算是不允许的。GCC有一个扩展,允许这样做。 - mtvecvoid*
算术运算。 - Sergey Podobry在 void*
指针上不允许使用指针算术运算。
void
是一种不完整类型,根据定义永远无法完成。 - schot将其转换为char指针并将指针向前移动x个字节。
man 3 qsort
中的描述,应该具有void qsort(void *base, size_t nmemb, size_t size, [snip])
参数。那么您就无法知道“正确的类型”。 - alisianoitypedef struct a_ { x X; y Y; } a;
...如果您有一个变量y *B =(something)
并且您想要指向B的封闭a
结构的指针(假设存在),那么您最终需要执行类似以下操作:...
a *A =(a *)(((char *)B) - offsetof(a,Y));
...如果您改为执行以下操作:...
a *A =(a *)(((x *)B)-1);
...那么您可能会遇到一些非常令人讨厌的意外! - chadjoan1
的类型。C11标准§6.2.5
第19段
void
类型包含一组空值;它是一个不完全的对象类型,无法完成。
以下程序在GCC编译器中运行良好。
#include<stdio.h>
int main()
{
int arr[2] = {1, 2};
void *ptr = &arr;
ptr = ptr + sizeof(int);
printf("%d\n", *(int *)ptr);
return 0;
}
其他编译器可能会生成错误。
正因为这个原因,您不能对 void *
类型进行指针运算!
空指针可以指向任何内存块。因此,当我们尝试对空指针进行指针算术运算时,编译器不知道要增加/减少多少字节。因此,在空指针参与任何指针算术运算之前,必须首先将其转换为已知类型。
void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation
char * c = (char *)p;
c++; // compiler will increment the c by 1, since size of char is 1 byte.
在进行指针运算之前,您需要将其强制转换为另一种指针类型。
指针算术在空指针中不允许。
原因:指针算术与普通算术不同,因为它是相对于基地址发生的。
解决方案:在进行算术运算时使用类型转换运算符,这将使表达式执行指针算术的基本数据类型得到确定。 例如:point 是空指针。
*point=*point +1; //Not valid
*(int *)point= *(int *)point +1; //valid
void *
上进行指针算术运算在语法上是非法的,不应该编译,并且如果编译了会产生未定义的行为。如果一个粗心的程序员可以通过禁用某些警告来使其编译,那也不能成为借口。 - underscore_dunsigned char*
更方便,例如为指针添加 sizeof
值。 - supercat
void
指针被递增,它如何指向后面x个字节?它不会。为什么那些有这种问题的人不能在提问之前测试一下呢,至少进行最基本的编译检查,而这个代码无法编译。给它负1分,真不敢相信这还获得了100个赞和0个踩。 - underscore_dvoid *
会使地址向前移动1个字节(与char *
相同)是完全合理的。然而,这种愚蠢不合逻辑的标准被无缘无故地强加给每个人,成为“非法”的。 - ScienceDiscovererp+n
显然必须将n * sizeof(*p)
添加到地址p
中,但sizeof(void)
显然为 0。 - Steve Summit