类型双关指的是将一种类型的representation重新解释为另一种类型.如果类型被保证具有相同或充分定义的表示,那么类型双关可能是可移植的.
这一点在6.2.5 Footnote 39(强调我)中得到了明确的证实:
相同的表示和对齐要求意味着可替换性,如函数的参数、函数的返回值和members of unions.
整数
对于有符号整数类型[...]作为值位的每一位应具有与对应的无符号类型的object representation位中的相同位相同的值(如果有符号类型中有M个值位且无符号类型中有N个值位,则M ≤ N).如果符号位为零,则不应影响结果值.
这意味着任何小于或等于相应signed
类型的最大正值的类型的正unsigned
值在类型穿孔时将具有相同的值,反之亦然,因为表示中的所有相应位必须对最终值具有相同的效果.
这是明确保证的:
符号位为零的有符号整数类型的有效(非trap )对象表示是对应无符号类型的有效对象表示,并且应表示相同的值.
此外:
对于任何整数类型,所有位为零的对象表示应是该类型中值零的表示.
所有位为0的任何值的值都为0.因此,任何全0整数类型的任何部分都可以被类型穿孔为任何较小的整数,或者多个所有位为0的较小整数(包括填充位,如果有的话)可以被类型穿孔为较大的整数,并且值为will still be 0.
This partially addresses the examples in the question, as we know the fixed-width types have no padding bits, so those examples shall work with values of 0.
固定宽度整数(如果定义)
对于相同的N
,键入intN_t
到uintN_t
将等同于在值if上加上2^(N-1)
,而intN_t
的值是负数.反之,等于从值if减go 2^(N-1)
,则uintN_t
的值大于最大值intN_t
.
typedef名称intN_t
指定宽度为N、无填充位和2的补数表示的带符号整数类型.因此,int8_t
表示宽度正好为8位的有符号整数类型.
这个要求保证了没有填充位,并且由于它们具有相同的总位数,intN_t
must be one中的value位数比uintN_t
中的value位数少.
将有恰好一个符号位.
并且由于intN_t
中的所有15个值位必须具有与uintN_t
表示中的相应位相同的值,并且对于所有固定宽度类型都需要2的补数,通过消除the sign bit in 100 must correspond to the value bit with value 103 in the 101.的过程,因此,它们之间的类型双关必须具有如上所述的可移植行为.
指针
倒数6.2.5:
指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求.同样,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求.所有指向 struct 类型的指针应具有彼此相同的表示和对齐要求.所有指向联合类型的指针应具有彼此相同的表示和对齐要求.指向其他类型的指针不必具有相同的表示或对齐要求.
这意味着人们可以安全地在void *
和char *
之间,或在任何两个struct
指针之间,或在任何两个union
指针之间,或在任何两个指针之间进行类型双关,以兼容(例如,相同类型的有符号和无符号版本)类型.虽然可以将任何对象指针类型转换为void *
或char *
,但这样做需要显式强制转换,而不是类型双关语.
struct
struct 和其他 struct 或类型之间的字体穿孔通常是不可移植的,因为在 struct 构件之间插入了未指定数量的填充物.但也有一些例外:
倒数6.5.2.3:
类型双关在一个 struct 和该 struct 的第一个成员之间,或者在一个联合和该联合中的任何成员之间是可移植的,前提是用最后存储的联合成员对成员进行类型双关的行为具有可移植的行为.
指向联合对象的指针,经过适当的转换,指向它的每个成员(或者如果成员是位字段,则指向它所在的单元),反之亦然.
指向 struct 对象的指针,经过适当转换,指向其初始成员(或者如果该成员是位字段,则指向其所在的单元),反之亦然.
此外:
一个特殊的保证是为了简化联合的使用:如果一个联合包含多个共享公共初始序列的 struct (见下文),并且如果联合对象当前包含这些 struct 中的一个,则允许判断其中任何一个 struct 的公共初始部分,只要有一个联合的完整类型的声明可见.如果对应的成员对于一个或多个初始成员的序列具有兼容的类型(对于位字段,宽度相同),则两个 struct 共享一个公共的初始序列.
这意味着,如果你有几个独立的 struct 类型,但它们的所有第一个成员都是兼容的类型,并且顺序相同,那么它们的匹配成员可以被类型punned/accessed,只要在访问的范围内,union
被完全声明并且两者都可见.C Standard个例子:
下面是一个有效的片段:
union {
struct {
int alltypes;
} n;
struct {
int type;
int intnode;
} ni;
struct {
int type;
double doublenode;
} nf;
} u;
u.nf.type = 1;
u.nf.doublenode = 3.14;
/* ... */
if (u.n.alltypes == 1)
if (sin(u.nf.doublenode) == 0.0)
/* ... */
下面是一个无效的片段(因为联合类型在函数f中不可见):
struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2)
{
if (p1->m < 0)
p2->m = -p2->m;
return p1->m;
}
int g()
{
union {
struct t1 s1;
struct t2 s2;
} u;
/* ... */
return f(&u.s1, &u.s2);
}
较小固定宽度类型的数组与较大固定宽度类型的数组之间的联合
除了位字段,对象由contiguous个由一个或多个字节组成的序列组成,其数量、顺序和编码要么是明确指定的,要么是实现定义的.
这意味着,如果没有填充位(固定宽度类型就是这种情况),两个连续类型之间的类型双关到两倍大的类型将保证具有连接其对象表示的位的效果.Contiguous意味着内存中的原始字节之间不可能有"垃圾".
对于无符号整数类型[...]这种类型的物体应能够
使用纯二进制表示来表示从0到2^N − 1
的值;
但是,pure binary notation是否能保证对象表示中的值位按大小顺序排列呢?
纯二进制表示法定义为:
整数的positional representation,使用二进制数字0和1,其中由successive bits表示的值是相加的,从1开始,并乘以连续的2的整数幂,可能除了最高位置的位.(改编自美国国家信息处理系统词典)一个字节包含CHAR_BIT位,而unsigned char类型的值范围从0到2CHAR_BIT − 1.
这明确地提到了representation, successive bits, and position.这意味着纯二进制符号中的位是从最低到最高排序的.如果这个were not the case,那么在表示中的位的确切位置将是毫无意义的,并且定义将不会提及位置或位是连续的,每个值位存在并且对应于0和N之间的每个2的幂.然而,这个定义指定位是连续的和有序的.
为什么要求有符号整数位与无符号值中的相应位具有相同的值,如果这将是多余的?最有可能的是,为了确保填充和/或符号位的放置不会相对于相应的符号类型"偏移"值位.
考虑到上述情况,将固定数量的有序位的相同副本连接到新的固定数量的有序位中,每次必须产生相同的值.可以提出一种情况,即任何不演示在这种情况下预期行为的实现都将违反pure binary notation.的定义,