为什么GCC在获取void表达式的地址时开始发出警告?
在几个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
的可执行文件。
这些天,我仍然可以这样做,但海湾合作委员会警告我:
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
似乎比组成某种类型更好。
那么为什么GCC突然决定开始警告我这个呢? 是否还有其他一些首选方式可以做到这一点?
似乎至少C11标准不允许这样:
6.3.2.1/1左值是一个表达式(对象类型不是void)可能指定一个对象。
如果您的表达式不是左值,则无法获取其地址。
声明的有效期
extern void _binary_foo_png_start;
是有问题的,因为它可以说没有声明一个对象(一个对象不能有类型void
)。 我尝试过的四个C编译器中有两个接受它。 其中一个编译器接受&_binary_foo_png_start
。 提交了一个错误。
从历史的角度来看,似乎曾经是允许这种结构的意图(这可以解释为什么Gcc过去接受它)在DR 12中可以找到类似的讨论。 请记住,相关定义( 如左值)在C90,C99和C11中有所不同。
C99规范保证char
sizeof(char) == 1
(6.5.3.4),它还声明“sizeof运算符产生其操作数的大小(以字节为单位)” – 因此char
可以用作表示字节的类型。
鉴于PNG图像也以字节排列,因此您应该使用char
(或相关: char*
或arrays-of)来表示以字节排列的任意二进制数据(例如PNG图像)。
因此,我会改变你的void _binary_foo_png_start;
to char _binary_foo_png_start
并且可能添加一个typedef char byte;
声明到共享头文件。
详细说明:“byte”是内存中最小的直接可寻址单元,一个字节不保证是8位(一个八位位组),它可能更大 – 但是如果一个字节大于8位,则可以预期在数据交换场景中,导入数据只会有“空位”,而不是沿着新的位级边界重新打包的数据(因此来自8位字节计算机的10个字节的数据仍会占用10个字节)一台10位机器(但使用100位而不是80位)。
来自ISO / IEC9899:
6.3.2.2无效
1不应以任何方式使用void表达式(具有void类型的表达式)的(不存在)值,并且不应对此类表达式应用隐式或显式转换(void除外)。 如果将任何其他类型的表达式计算为void表达式,则会丢弃其值或指示符。 (评估void表达式的副作用。)
所以你的问题为什么他们开始警告它:
因为他们开始能够检测到无效的void使用情况。