【问题标题】:trap representation陷阱表示
【发布时间】:2011-10-07 05:40:34
【问题描述】:
  1. 什么是 C 中的“陷阱表示”(一些示例可能会有所帮助)?这适用于 C++ 吗?

  2. 鉴于此代码...

    float f=3.5;
    int *pi = (int*)&f;
    

    ...假设sizeof(int) == sizeof(float),那么f*pi 是否具有相同的二进制表示/模式?

【问题讨论】:

    标签: c++ c visual-studio-2010 gcc language-lawyer


    【解决方案1】:
    1. 陷阱表示是 C99(IIRC 而非 C89)使用的一个包罗万象的术语,用于描述适合某个类型占用的空间的位模式,但如果用作该类型的值则会触发未定义的行为。定义在第 6.2.6.1p5 节中(触角包含在所有 6.2.6 中),我不会在这里引用它,因为它很长而且令人困惑。存在这种位模式的类型被称为“具有”陷阱表示。任何类型都不需要具有任何陷阱表示,但标准保证不会具有陷阱表示的唯一类型是unsigned char (6.2.6.1p5, 6.2.6.2p1)。

      该标准给出了两个假设的陷阱表示示例,它们都与任何实际 CPU 多年来所做的任何事情都不对应,因此我不会将您与它们混淆。陷阱表示的一个示例(也是您可能遇到的任何 CPU 上的硬件级陷阱表示唯一)是一个信号 NaN 在浮点型。 C99 附录 F(第 2.1 节)明确未定义信号 NaN 的行为,尽管 IEC 60559 详细说明了它们的行为。

      值得一提的是,虽然指针类型允许有陷阱表示,但空指针不是陷阱表示。空指针只有在被取消引用或偏移时才会导致未定义的行为;对它们的其他操作(最重要的是,比较和复制)是明确定义的。如果您仅使用具有陷阱表示的类型读取陷阱表示,则会导致未定义的行为。 (invalid 但非空指针是否被认为是或者应该被认为是陷阱表示是一个争论的主题。CPU 不会那样对待它们,但编译器可能会。)

    2. 您显示的代码具有未定义的行为,但这是因为指针别名规则,而不是因为陷阱表示。这是如何将float 转换为具有相同表示的int(假设,如您所说,sizeof(float) == sizeof(int)

      int extract_int(float f)
      {
          union { int i; float f; } u;
          u.f = f;
          return u.i;
      }
      

      这段代码在 C99 中有 未指定(不是未定义)的行为,这基本上意味着标准没有定义 产生什么整数值,但你确实得到了 一些有效的整数值,它不是陷阱表示,并且不允许编译器在假设您没有这样做的情况下进行优化。 (第 6.2.6.1 节,第 7 段。我的 C99 副本可能包含技术勘误——我记得这 在原始出版物中未定义,但在 TC 中已更改为未指定。)

    【讨论】:

    • 确实是 C99 上的 UB(参见附录 J),这可能是一个疏忽(其他地方的措辞似乎另有说明)。在 C1x 中不再是 UB,而且措辞更加清晰。
    • C99 中此问题的缺陷报告/TC:www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htmff
    • IA64 有一个用于整数的陷阱表示,称为“Not a Thing” (NaT)。请参阅open-std.org/jtc1/sc22/wg14/www/docs/n1208.htmblogs.msdn.com/b/oldnewthing/archive/2004/01/19/60162.aspx 了解更多信息。
    • 如果您仔细阅读您引用的缺陷报告,您会发现 ia64 的 NaT 不是实际上是符合 C99 的陷阱表示(DR 要求进行更改以使其成为一个,但从未真正发生过的AFAICT)。类型的 C99 陷阱表示必须是适合为该类型分配的可见空间的位模式; NaT 是带外的。这是 NaT 设计不佳的众多方式之一。您引用的旧新事物博客说明了另一种这样的方式。
    • @supercat 我想你刚刚描述了int 的陷阱表示的行为。 unsigned char 本身不能有陷阱表示,并且任何其他类型的陷阱表示都特别允许使用“具有字符类型的左值表达式”来读取。 (C99 6.2.6.1p5)
    【解决方案2】:

    使用指向 int 的指针对浮点数起别名的未定义行为。

    【讨论】:

    • 我同意它是 UB,违反了严格的别名规则。我问这个是因为我相信它适用于大多数编译器。在此处查看 Chris Lutz 的回答:stackoverflow.com/questions/1121160/…
    • @Burt:然后标记编译器并在问题中指定它们。
    • @Burt:由于严格的别名规则,使用 int 对浮点数进行别名是未定义的行为,并且不能假定“适用于大多数编译器”。但是,char* 可以为任何类型设置别名,这将使其仅仅是实现定义的行为。或者,如果您使用 GCC,您可以使用 __attribute__((may_alias))
    【解决方案3】:

    一般来说,任何非陷阱 IEEE-754 浮点值都可以在某些平台上表示为整数,没有任何问题。但是,如果您假设 所有 浮点值具有唯一的整数表示 并且 您碰巧强制 FPU 加载价值。

    (示例取自http://www.dmh2000.com/cpp/dswap.shtml

    例如,在处理 FP 数据时,您需要在具有不同字节序的 CPU 之间进行封送处理,您可能会考虑执行以下操作:

    double swap(double)

    不幸的是,如果编译器将输入加载到 FPU 寄存器并且它是一个陷阱表示,则 FPU 可以用一个恰好是不同位表示的等效陷阱表示将其写回。

    换句话说,如果您没有正确转换,有些 FP 值没有相应的位表示(正确的意思是通过unionmemcpy 通过char * 或其他标准机制) .

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 2012-02-22
      • 2016-12-18
      相关资源
      最近更新 更多