要理解无效指针

gsa*_*ras 8 c gcc pointers void

在我的回答中,我提到解除引用void指针是一个坏主意.但是,当我这样做时会发生什么?

#include <stdlib.h>
int main (void) {
 void* c = malloc(4);
 *c;
 &c[0];
}
Run Code Online (Sandbox Code Playgroud)

汇编:

gcc prog.c -Wall -Wextra
prog.c: In function 'main':
prog.c:4:2: warning: dereferencing 'void *' pointer
  *c;
  ^~
prog.c:5:4: warning: dereferencing 'void *' pointer
  &c[0];
    ^
prog.c:5:2: warning: statement with no effect [-Wunused-value]
  &c[0];
  ^
Run Code Online (Sandbox Code Playgroud)

这是来自Wandbox的图片,对于那些说它没有发生的人:

在此输入图像描述

Live demoIdeone中的一个.

它实际上会尝试读取c指向的内存,然后获取该结果,但实际上什么都不做?或者这条线根本没有效果(但GCC不会产生警告).

我在想,既然编译器对数据类型一无所知,在不知道类型大小的情况下,它将无法做很多事情.

为什么derefencing a void*不会产生错误,只是警告?


如果我尝试作业,我会收到错误:

无效使用void表达式

但不应该单独解除引用会产生错误吗?

Ant*_*ala 12

C标准在5.1.1.3p1中明确说明:

如果预处理转换单元或转换单元包含违反任何语法规则或约束的情况,则符合要求的实现应生成至少一条诊断消息(以实现定义的方式标识),即使该行为也明确指定为未定义或实现 -定义.在其他情况下不需要产生诊断消息.9)

用脚注9说

目的是实施应确定每次违规的性质,并在可能的情况下进行本地化.当然,只要仍然正确地翻译了有效的程序,实现就可以自由地产生任意数量的诊断.它也可能成功翻译无效程序.

因此,GCC符合C标准的字母.您的程序是无效的程序.只需要一条诊断消息 - 允许编译器成功翻译您的无效程序.由于GCC具有无效指针算法的非标准扩展:

在GNU C中,void指向函数指针和指针的指针都支持加法和减法操作.这是通过将a void或函数的大小视为1.

这样做的结果是,函数类型和返回sizeof也允许使用void和返回1.

-Wpointer-arith如果使用这些扩展,该选项会发出警告.

它决定它可能会对你的无效程序做一些"合理的"并成功翻译它.


请注意,已经需要非求值取消引用指向void指针sizeof,因为:

void *foo;
sizeof *foo;
Run Code Online (Sandbox Code Playgroud)

必须匹配

sizeof (void);
Run Code Online (Sandbox Code Playgroud)

它们都评估为1,因此更容易让丢弃的指针取消引用到处无效.


正如Lundin所说,如果您想要实现约束违规的实际错误,请使用-std=c11 -pedantic-errors.

  • @Lundin兼容编译器可以成功编译代码.只需要生成诊断.因此`-pedantic`足以实现合规. (3认同)
  • @Lundin正如这个答案明确指出的那样,只需要**诊断消息**,因此警告符合标准. (2认同)
  • @Lundin,警告是一条诊断消息 (2认同)

Mic*_*urr 5

从C11 6.3.2.3"无效":

void表达式(具有void类型的表达式)的(不存在)值不得以任何方式使用,并且隐式或显式转换(void除外)不应应用于此类表达式.如果将任何其他类型的表达式计算为void表达式,则会丢弃其值或指示符.(评估void表达式的副作用.)

所以表达式可以有void类型,你只是不能对该表达式的结果做任何事情(例如将它赋值给某些东西).*c是一个无效的有效表达式.

6.2.5/19"类型"

void类型包含一组空值; 它是一个不完整的对象类型,无法完成.

6.5.6/2"添加剂操作员"

另外,两个操作数都应具有算术类型,或者一个操作数应是指向完整对象类型的指针,另一个操作数应具有整数类型.

数组下标是根据指针算法定义的.通常情况下,&c[0]不允许表达式.

GCC允许将指针算法(以及因此下标数组)的void类型作为扩展.