为什么GCC在获取void表达式的地址时开始发出警告?

时间:2014-12-03 03:53:47

标签: c gcc

以前有几个GCC版本,我可以做这样的事情:

$ objcopy -I binary -O elf64-x86-64 -B i386 foo.png foo.png.o

...在C中加入以下内容,作为SDL图像加载的示例:

extern void _binary_foo_png_start;
extern void _binary_foo_png_start;
SDL_Surface *image = IMG_Load_RW(SDL_RWFromMem(&_binary_foo_png_start, &_binary_foo_png_end));

然后我将foo.png.o与C文件中的目标文件链接起来,并获得一个整齐地包含foo.png的可执行文件。

这些天,我仍然可以这样做,但GCC警告我:

foo.c:57:19: warning: taking address of expression of type ‘void’
foo.c:57:44: warning: taking address of expression of type ‘void’

显然它仍然有效,据我所知,它确实做到了它应该做的事情。符号本身没有明确定义的类型,因此将它们声明为void似乎是合适的。我的意思是,当然,我可能也可以给他们任何其他任意类型,它仍然可以正常工作,因为我只是想要他们的地址,但声明他们void似乎比组成某种类型更好。

那么为什么海湾合作委员会突然决定开始警告我这个?还有其他一些首选的方法吗?

3 个答案:

答案 0 :(得分:5)

似乎至少C11标准不允许这样做:

  

6.3.2.1/1 Lvalue是一个潜在的表达式(对象类型不是void)   指定一个对象。

如果您的表达式不是左值,则无法获取其地址。

声明的有效期

extern void _binary_foo_png_start;

是有问题的,因为它可以说没有声明一个对象(一个对象不能有void类型)。我试过的四个C编译器中有两个接受它。其中一个编译器接受&_binary_foo_png_start。提交了一个错误。

在一个历史性的说明中,似乎曾经是允许这种结构的意图(这可以解释为什么Gcc曾经接受它)在DR 12中可以找到类似的讨论。请记住, lvalue 等相关定义在C90,C99和C11中有所不同。

答案 1 :(得分:2)

C99规范保证char具有sizeof(char) == 1(6.5.3.4),它也声明" sizeof运算符产生其操作数的大小(以字节为单位)" - 因此char可以用作表示字节的类型。

鉴于PNG图像也以字节排列,因此您应该使用char(或相关:char*或数组)来表示以字节为单位排列的任意二进制数据(例如PNG图像)。

因此,我将您的void _binary_foo_png_start;更改为char _binary_foo_png_start,并可能在共享头文件中添加typedef char byte;语句。

详细说明:a" byte"内存中最小的直接可寻址单元,一个字节不保证是8位(一个八位位组),它可能更大 - 但是如果一个字节大于8位,可以预期在导入数据的数据交换场景中只会有"空位"而不是沿着新的位级边界重新打包数据(因此来自8位字节计算机的10个字节的数据仍会占用10位的10个字节机器(但使用100位而不是80位)。

答案 2 :(得分:2)

来自ISO / IEC9899:

  

6.3.2.2 void

     

1 void表达式(具有void类型的表达式)的(不存在)值不应该   以任何方式使用,隐含或显式转换(无效除外)不得   适用于这样的表达。如果任何其他类型的表达式被评估为void   表达式,其值或指示符被丢弃。 (评估void表达式   副作用。)

所以你的问题为什么他们开始警告它:

因为他们开始能够检测到无效的虚空使用。