当指向特定类型(例如int
, char
, float
, ..)的指针增加时,它的值会增加该数据类型的大小。如果void
指向大小数据的指针x
递增,它如何指向x
前面的字节?编译器如何知道添加x
指针的值?
9 回答
最终结论:对 a 的算术在C 和 C++ 中都是void*
非法的。
GCC 允许它作为扩展,请参阅Arithmetic on void
- 和 Function-Pointers(请注意,本节是手册“C 扩展”一章的一部分)。void*
为了与 GCC 兼容,Clang 和 ICC 可能允许算术。其他编译器(例如 MSVC)不允许对 进行算术运算void*
,如果-pedantic-errors
指定了标志,或者指定了-Werror-pointer-arith
标志(如果您的代码库也必须使用 MSVC 编译,则此标志很有用)。
C标准说话
报价来自 n1256 草案。
该标准对加法操作的描述如下:
6.5.6-2:对于加法,两个操作数都应具有算术类型,或者一个操作数应是指向对象类型的指针,而另一个应具有整数类型。
因此,这里的问题是是否void*
是指向“对象类型”的指针,或者等效地,是否void
是“对象类型”。“对象类型”的定义是:
6.2.5.1:类型分为对象类型(完全描述对象的类型)、函数类型(描述函数的类型)和不完整类型(描述对象但缺少确定其大小所需信息的类型)。
标准定义void
为:
6.2.5-19:
void
类型包含一组空值;它是一个不完整的类型,无法完成。
由于void
是不完整类型,因此它不是对象类型。因此,它不是加法运算的有效操作数。
因此,您不能对void
指针执行指针算术。
笔记
void*
最初,由于 C 标准的以下部分,人们认为算术是允许的:
6.2.5-27:指向 void 的指针应具有与 指向字符类型的指针相同的表示和对齐要求。
然而,
相同的表示和对齐 要求意味着作为函数的参数、函数的返回值和联合成员的可互换性。
所以这意味着无论有类型还是printf("%s", x)
具有相同的含义,但这并不意味着您可以对 a 进行算术运算。x
char*
void*
void*
指针上不允许指针算术void*
。
将其转换为 char 指针,将指针向前增加 x 个字节。
C 标准不允许void指针算术。然而,考虑到void is的大小, GNU C是允许的。1
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 指针可以指向任何内存块。因此,当我们尝试对 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 指针中不允许指针算术。
原因:指针算术与普通算术不同,因为它是相对于基地址发生的。
解决方案:在算术时使用类型转换运算符,这将使执行指针算术的表达式知道基本数据类型。例如:point 是 void 指针
*point=*point +1; //Not valid
*(int *)point= *(int *)point +1; //valid
编译器通过类型转换知道。给定一个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)