C语言中的“陷阱表示”是什么(某些示例可能会有所帮助)?这适用于C ++吗?
给定此代码...
float f=3.5; int *pi = (int*)&f;
...和假设
sizeof(int) == sizeof(float)
,做f
和*pi
有相同的二进制表示/模式?
Answers:
陷阱表示是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不会以这种方式对待它们,但是编译器可以。)
您显示的代码具有未定义的行为,但这是由于指针别名规则,而不是由于陷阱表示。这是将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)。
int
。 unsigned char
本身不能具有陷阱表示,并且特别允许使用“具有字符类型的左值表达式”读取任何其他类型的陷阱表示。(C99 6.2.6.1p5)
带有指向int的指针的float别名的不确定行为。
char*
可以别名任何类型,这将使其仅实现定义的行为。另外,__attribute__((may_alias))
如果您使用的是GCC,则可以使用。
通常,在某些平台上,任何非陷阱IEEE-754浮点值都可以表示为整数,而不会出现任何问题。但是,如果您假定所有浮点值都具有唯一的整数表示形式,并且存在浮点值,则可能导致意外的行为。碰巧迫使FPU加载该值。
(示例取自http://www.dmh2000.com/cpp/dswap.shtml)
例如,当使用FP数据时,您需要在字节序不同的CPU之间进行封送处理,您可能会考虑执行以下操作:
double swap(double)
不幸的是,如果编译器将输入加载到FPU寄存器中并且是陷阱表示,则FPU可以使用碰巧是另一位表示形式的等效陷阱表示将其写回。
换句话说,如果您未正确转换(通过正确的方法是指通过union
,memcpy
通过char *
或其他标准机制),则有些FP值没有相应的位表示形式。