【发布时间】:2011-10-07 05:40:34
【问题描述】:
什么是 C 中的“陷阱表示”(一些示例可能会有所帮助)?这适用于 C++ 吗?
-
鉴于此代码...
float f=3.5; int *pi = (int*)&f;...假设
sizeof(int) == sizeof(float),那么f和*pi是否具有相同的二进制表示/模式?
【问题讨论】:
标签: c++ c visual-studio-2010 gcc language-lawyer
什么是 C 中的“陷阱表示”(一些示例可能会有所帮助)?这适用于 C++ 吗?
鉴于此代码...
float f=3.5;
int *pi = (int*)&f;
...假设sizeof(int) == sizeof(float),那么f 和*pi 是否具有相同的二进制表示/模式?
【问题讨论】:
标签: c++ c visual-studio-2010 gcc language-lawyer
陷阱表示是 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 不会那样对待它们,但编译器可能会。)
您显示的代码具有未定义的行为,但这是因为指针别名规则,而不是因为陷阱表示。这是如何将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 的陷阱表示的行为。 unsigned char 本身不能有陷阱表示,并且任何其他类型的陷阱表示都特别允许使用“具有字符类型的左值表达式”来读取。 (C99 6.2.6.1p5)
使用指向 int 的指针对浮点数起别名的未定义行为。
【讨论】:
char* 可以为任何类型设置别名,这将使其仅仅是实现定义的行为。或者,如果您使用 GCC,您可以使用 __attribute__((may_alias))。
一般来说,任何非陷阱 IEEE-754 浮点值都可以在某些平台上表示为整数,没有任何问题。但是,如果您假设 所有 浮点值具有唯一的整数表示 并且 您碰巧强制 FPU 加载价值。
(示例取自http://www.dmh2000.com/cpp/dswap.shtml)
例如,在处理 FP 数据时,您需要在具有不同字节序的 CPU 之间进行封送处理,您可能会考虑执行以下操作:
double swap(double)
不幸的是,如果编译器将输入加载到 FPU 寄存器并且它是一个陷阱表示,则 FPU 可以用一个恰好是不同位表示的等效陷阱表示将其写回。
换句话说,如果您没有正确转换,有些 FP 值没有相应的位表示(正确的意思是通过union、memcpy 通过char * 或其他标准机制) .
【讨论】: