TLDR;以下代码是否调用未定义(或未指定)的行为?
#include <stdio.h>
#include <string.h>
void printme(void *c, size_t n)
{
/* print n bytes in binary */
}
int main() {
long double value1 = 0;
long double value2 = 0;
memset( (void*) &value1, 0x00, sizeof(long double));
memset( (void*) &value2, 0x00, sizeof(long double));
/* printf("value1: "); */
/* printme(&value1, sizeof(long double)); */
/* printf("value2: "); */
/* printme(&value2, sizeof(long double)); */
value1 = 0.0;
value2 = 1.0;
printf("value1: %Lf\n", value1);
printme(&value1, sizeof(long double));
printf("value2: %Lf\n", value2);
printme(&value2, sizeof(long double));
return 0;
}
在我的 x86-64 机器上,输出取决于传递给编译器的特定优化标志(gcc-4.8.0、-O0 与 -O1)。
用-O0,我得到
value1: 0.000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
value2: 1.000000
00000000 00000000 00000000 00000000 00000000 00000000 00111111 11111111
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
在使用 -O1 时,我得到了
value1: 0.000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
value2: 1.000000
00000000 00000000 00000000 00000000 00000000 01000000 00111111 11111111
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
请注意倒数第二行多出来的 1。此外,在 memset 后取消注释打印指令会使 1 消失。这似乎依赖于两个事实:
- long double 被填充,即 sizeof(long double) = 16 但只使用了 10 个字节。
- 对 memset 的调用可能会被优化掉
- long double 的填充位可能会更改,恕不另行通知,即 value1 和 value2 上的浮点运算似乎会打乱填充位。
我正在使用 -std=c99 -Wall -Wextra -Wpedantic
进行编译,但没有收到任何警告,所以我不确定这是不是严格违反别名的情况(但很可能是) .传递 -fno-strict-aliasing
不会改变任何事情。
上下文是在描述 here 的 HDF5 库中发现的错误. HDF5 做了一些摆弄来找出浮点类型的 native 位表示,但如果填充位不保持为零,它会感到困惑。
所以:
- 这是未定义的行为吗?
- 这是严格的别名违规吗?
谢谢。
编辑:这是 printme 的代码。我承认我只是从某个地方剪切和粘贴而没有太在意它。如果问题出在这里,我会脱下裤子围着 table 走。
void printme(void *c, size_t n)
{
unsigned char *t = c;
if (c == NULL)
return;
while (n > 0) {
int q;
--n;
for(q = 0x80; q; q >>= 1)
printf("%x", !!(t[n] & q));
printf(" ");
}
printf("\n");
}
最佳答案
虽然 C 标准允许操作破坏填充位,但我认为这不是您的系统上正在发生的事情。相反,它们从一开始就不会被初始化,并且 GCC 只是优化 -O1
处的 memset
,因为该对象随后会被覆盖。这可能会被 -fno-builtin-memset
抑制。
关于c - 访问长双位表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18668871/