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 doubles 的填充位可能会在没有通知的情况下发生变化,即 value1 和 value2 上的浮点运算似乎会扰乱填充位。
我正在编译-std=c99 -Wall -Wextra -Wpedantic
并且没有收到任何警告,所以我不确定这是一个严格的混叠违规情况(但很可能是)。通过-fno-strict-aliasing
并没有改变任何事情。
上下文是在此处描述的 HDF5 库中发现的错误。HDF5 在计算浮点类型的本机位表示方面做了一些调整,但如果填充位不保持为零,它就会感到困惑。
所以:
- 这是未定义的行为吗?
- 这是一个严格的混叠违规吗?
谢谢。
编辑:这是 printme 的代码。我承认我只是从某个地方剪切和粘贴而没有过多关注它。如果问题出在这儿,我会脱下裤子绕过桌子。
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");
}