陷阱表示


74
  1. C语言中的“陷阱表示”是什么(某些示例可能会有所帮助)?这适用于C ++吗?

  2. 给定此代码...

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

    ...和假设sizeof(int) == sizeof(float),做f*pi有相同的二进制表示/模式?

Answers:


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

    该标准给出了两个陷阱表示的假设示例,它们都不与任何实际CPU多年以来所做的任何事情相对应,因此我不会将它们与它们混淆。陷阱表示的一个很好的例子(也是唯一可能在任何可能遇到的CPU上都具有硬件级别陷阱表示的东西)是浮点类型的信号NaN。即使IEC 60559详细说明了它们的行为,C99附录F(第2.1节)也明确保留了信号NaN行为的不确定性。

    值得一提的是,当指针类型允许有陷阱表示,空指针是不是陷阱表示。空指针仅在被取消引用或偏移时才导致未定义的行为。对它们的其他操作(最重要的是比较和副本)是明确定义的。如果仅使用具有陷阱表示形式的类型读取陷阱表示形式,则会导致未定义的行为。(是否还是应该将无效但非空的指针视为陷阱表示形式是一个有争议的话题。CPU不会以这种方式对待它们,但是编译器可以。)

  2. 您显示的代码具有未定义的行为,但这是由于指针别名规则,而不是由于陷阱表示。这是将afloat转换为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的副本可能包括技术corrigienda -我的回忆是,这在原来的出版物不确定,但改为不确定的TC)。


2
实际上,它是C99上的UB(请参阅附录J),这可能是一个疏忽(其他地方的措辞似乎暗示了这一点)。在C1x中,它不再是UB,其用语也变得更加清晰。
ninjalj 2011年

2
C99中此问题的缺陷报告/ TC:www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htmff
u0b34a0f6ae 2011年

3
IA64具有称为“ Not Thing”(NaT)的整数的陷阱表示。有关更多信息,请参阅open-std.org/jtc1/sc22/wg14/www/docs/n1208.htmblogs.msdn.com/b/oldnewthing/archive/2004/01/19/60162.aspx
亚当·罗森菲尔德

3
如果你读你仔细举缺陷报告,你会发现,IA64的NAT是不是实际上是一个C99兼容陷阱表示(灾难恢复要求的变化,使之成为一个,但AFAICT从未实际发生)。一个类型的C99陷阱表示形式必须是适合于为该类型分配的可见空间的位模式。NaT是带外的。这是NaT不良设计的多种方式之一。您引用的Old New Thing博客说明了另一种方法。
zwol 2012年

1
@supercat我认为您刚刚描述了陷阱表示的行为intunsigned char本身不能具有陷阱表示,并且特别允许使用“具有字符类型的左值表达式”读取任何其他类型的陷阱表示。(C99 6.2.6.1p5)
zwol13年

7

带有指向int的指针的float别名的不确定行为。


我同意这是UB,违反了严格的别名规则。我之所以这样问,是因为我相信它适用于大多数编译器。看到克里斯·卢茨(Chris Lutz)的答案在这里:stackoverflow.com/questions/1121160/…–
Burt

@Burt:然后标记编译器并在问题中指定它们。
小狗

4
@Burt:由于严格的别名规则,使用int对float进行别名是未定义的行为,并且不能假定为“对大多数编译器都有效”。但是,char*可以别名任何类型,这将使​​其仅实现定义的行为。另外,__attribute__((may_alias))如果您使用的是GCC,则可以使用。
乔伊·亚当斯

3

通常,在某些平台上,任何非陷阱IEEE-754浮点值都可以表示为整数,而不会出现任何问题。但是,如果您假定所有浮点值都具有唯一的整数表示形式,并且存在浮点值,则可能导致意外的行为。碰巧迫使FPU加载该值。

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

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

double swap(double)

不幸的是,如果编译器将输入加载到FPU寄存器中并且是陷阱表示,则FPU可以使用碰巧是另一位表示形式的等效陷阱表示将其写回。

换句话说,如果您未正确转换(通过正确的方法是指通过unionmemcpy通过char *或其他标准机制),则有些FP值没有相应的位表示形式。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.