c - 访问长双位表示

标签 c strict-aliasing long-double

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 消失。这似乎依赖于两个事实:

  1. long double 被填充,即 sizeof(long double) = 16 但只使用了 10 个字节。
  2. 对 memset 的调用可能会被优化掉
  3. long double 的填充位可能会更改,恕不另行通知,即 value1 和 value2 上的浮点运算似乎会打乱填充位。

我正在使用 -std=c99 -Wall -Wextra -Wpedantic 进行编译,但没有收到任何警告,所以我不确定这是不是严格违反别名的情况(但很可能是) .传递 -fno-strict-aliasing 不会改变任何事情。

上下文是在描述 here 的 HDF5 库中发现的错误. HDF5 做了一些摆弄来找出浮点类型的 native 位表示,但如果填充位不保持为零,它会感到困惑。

所以:

  1. 这是未定义的行为吗?
  2. 这是严格的别名违规吗?

谢谢。

编辑:这是 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/

相关文章:

c++ - "aggregate or union type that includes one of the aforementioned types"严格别名规则发生了什么?

c - 长双数学库实现?

c++ - С++:如何将long double转换为char [12]?

c++ - Eigen::MatrixXd 类型定义的替换

c - GBA 中的初始化程序无效

在线性时间内用 crt 连接 C 字符串

c - Unix环境运行C程序

c++ - 通过两个不同的基类引用一个对象是否违反了严格的别名规则?

c++11 - 从一种类型到另一种类型的 memcpy。之后我们如何访问目的地?

c - 通过main传入并设置global int