什么是 C 中的“陷阱表示”(一些示例可能会有所帮助)?这适用于 C++ 吗?
鉴于此代码...
float f=3.5; int *pi = (int*)&f;
...并假设
sizeof(int) == sizeof(float)
,是否具有相同的二进制表示/模式?f
*pi
3 回答
陷阱表示是 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 详细说明了它们的行为。
值得一提的是,虽然指针类型允许有陷阱表示,但空指针不是陷阱表示。空指针只有在被取消引用或偏移时才会导致未定义的行为;对它们的其他操作(最重要的是,比较和复制)是明确定义的。如果仅使用具有陷阱表示的类型读取陷阱表示,则会导致未定义的行为。(无效但非空指针是否或应该被视为陷阱表示是一个争论的主题。CPU 不会那样对待它们,但编译器可能会。)
您显示的代码具有未定义的行为,但这是因为指针别名规则,而不是因为陷阱表示。这是如何将 a
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 中更改为未指定。)
使用指向 int 的指针对浮点数进行别名的未定义行为。
通常,任何非陷阱 IEEE-754 浮点值都可以在某些平台上表示为整数,而不会出现任何问题。但是,如果您假设所有浮点值都具有唯一的整数表示,并且您碰巧强制 FPU 加载该值,则存在可能导致意外行为的浮点值。
(示例取自http://www.dmh2000.com/cpp/dswap.shtml)
例如,在处理 FP 数据时,您需要在具有不同字节顺序的 CPU 之间编组,您可能会考虑执行以下操作:
double swap(double)
不幸的是,如果编译器将输入加载到 FPU 寄存器中并且它是一个陷阱表示,那么 FPU 可以用一个恰好是不同位表示的等效陷阱表示将其写回。
换句话说,如果您没有正确转换(正确的意思是通过 a union
、memcpy
viachar *
或其他标准机制),则有些 FP 值没有相应的位表示。