当指向特定类型(例如int, char, float, ..)的指针增加时,其值将按该数据类型的大小增加。如果指向大小为x的数据的空指针被增加,它如何提前x字节指向?编译器如何知道将x添加到指针的值?


不允许对void*指针进行指针算术运算。


你不能在void *类型上做指针算术,正是因为这个原因!


将其转换为char型指针,使指针向前增加x字节。


在进行指针算术之前,必须将它强制转换为另一种类型的指针。


最后的结论:在C和c++中,对void*的运算都是非法的。

GCC允许它作为一个扩展,参见算术关于void-和function - pointer(注意,这部分是手册的“C Extensions”章节的一部分)。Clang和ICC可能为了与GCC兼容而允许void*算法。其他编译器(如MSVC)不允许对void*进行算术运算,如果指定了-pedantic-errors标志,或者指定了-error -pointer-arith标志,GCC就不允许对void*进行算术运算(如果你的代码基也必须使用MSVC编译,这个标志很有用)。

C标准说话

引用自n1256草案。

该标准对加法运算状态的描述:

6.5.6-2:对于加法,两者都可以 操作数应具有算术类型, 或者一个操作数是指向的指针 一个对象类型,另一个应该 具有整型。

所以,这里的问题是void*是否是一个指向“对象类型”的指针,或者等效地,void是否是一个“对象类型”。“对象类型”的定义是:

6.2.5.1:类型被划分为对象类型(完全描述对象的类型)、函数类型(描述函数的类型)和不完整类型(描述对象但缺乏确定其大小所需信息的类型)。

标准将void定义为:

6.2.5-19:虚空类型包括 一组空值; 它是一个不完全类型,不能 完成。

由于void是不完全类型,所以它不是对象类型。因此,它不是加法运算的有效操作数。

因此,不能在空指针上执行指针算术。

笔记

最初,人们认为void*算术是允许的,因为C标准的这些部分:

6.2.5-27:指向void的指针应具有相同的表示和对齐方式 的指针 字符类型。

然而,

相同的表示和对齐方式 需求意味着暗示 作为参数的互换性 函数,返回值 职能和工会成员。

因此,这意味着printf("%s", x)无论x的类型是char*还是void*都具有相同的含义,但这并不意味着您可以对void*进行算术运算。


Void指针可以指向任何内存块。因此,当我们尝试对空指针进行指针算术时,编译器不知道要增加/减少多少字节。因此,必须首先将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.

编译器通过类型转换知道。给定一个void *x:

X +1向X增加一个字节,指针指向字节X +1 (int*)x+1添加sizeof(int)字节,指针指向字节x+ sizeof(int) (float*)x+1地址sizeof(float)字节, 等。

尽管第一项是不可移植的,并且反对C/ c++的Galateo,但它仍然是C语言正确的,这意味着它将在大多数编译器上编译成可能需要适当标志的东西(如-Wpointer-arith)


C标准不允许空指针运算。然而,考虑到void的大小为1,GNU C是允许的。

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指针中不允许进行指针算术运算。

原因:指针算术与普通算术不同,因为它是相对于基址发生的。

解决方案:在进行算术时使用类型强制转换操作符,这将使执行指针算术的表达式的基本数据类型已知。 例如:point是空指针

*point=*point +1; //Not valid
*(int *)point= *(int *)point +1; //valid

[从后面一个重复问题的评论中抄来的答案]

允许对空指针进行算术运算是一个有争议的、非标准的扩展。如果你在汇编语言中思考,指针只是地址,空指针的算术是有意义的,加1只是加1。但如果你用C语言来思考,使用C语言的指针算术模型,给任何指针p加1实际上是在地址上加sizeof(*p),这就是你想要的指针算术,但由于sizeof(void)为0,它对于void指针就失效了。

如果你用C语言思考,你不介意它被打破,你不介意插入显式类型转换到(char *),如果这是你想要的算术。但如果你在汇编程序中思考,你希望它只是工作,这就是为什么扩展(尽管偏离了C中指针算术的正确定义)在某些领域是可取的,并且由一些编译器提供。