Tav*_*nes 5 c c++ language-lawyer
C 和 C++ 标准都允许相同整数类型的有符号和无符号变体互为别名。例如,unsigned int*和int*5别名。但这并不是故事的全部,因为它们显然具有不同范围的可表示值。我有以下假设:
unsigned int通过 an 读取 an int*,则该值必须在 的范围内,int否则会发生整数溢出且行为未定义。这样对吗?int是通过 an 读取的unsigned int*,负值会环绕,就好像它们被强制转换为unsigned int。这样对吗?int和unsigned int,通过任一类型的指针访问它被完全定义,并给出了相同的值。这样对吗?此外,兼容但不等效的整数类型呢?
int和long具有相同范围、对齐方式等的系统上,可以int*和long*别名吗?(我认为不是。)char16_t*和uint_least16_t*别名吗?我怀疑这在 C 和 C++ 之间有所不同。在 C 中,char16_t是uint_least16_t(正确的?)的 typedef 。在 C++ 中,char16_t是它自己的原始类型,它与uint_least16_t. 与 C 不同,C++ 似乎也不例外,允许兼容但不同的类型别名。\n\n\n如果
\nint通过 an 读取 anunsigned int*,则负值会环绕,就像它们被转换为 一样unsigned int。它是否正确?
对于使用二进制补码的系统,类型双关和有符号到无符号转换是等效的,例如:
\n\nint n = ...;\nunsigned u1 = (unsigned)n;\nunsigned u2 = *(unsigned *)&n;\nRun Code Online (Sandbox Code Playgroud)\n\n这里, 和 都u1具有u2相同的值。这是迄今为止最常见的设置(例如,Gcc 为其所有目标记录了此行为)。然而,C 标准还使用补码或符号数值来表示有符号整数。在这样的实现中(假设没有填充位并且没有陷阱表示),整数值的转换和类型双关的结果可以产生不同的结果。作为一个例子,假设符号大小并被n初始化为-1:
int n = -1; /* 10000000 00000001 assuming 16-bit integers*/\nunsigned u1 = (unsigned)n; /* 11111111 11111111\n effectively 2's complement, UINT_MAX */\nunsigned u2 = *(unsigned *)&n; /* 10000000 00000001\n only reinterpreted, the value is now INT_MAX + 2u */\nRun Code Online (Sandbox Code Playgroud)\n\n转换为无符号类型意味着比该类型的最大值加/减 1,直到该值在范围内。取消引用转换后的指针只是重新解释位模式。换句话说,初始化中的转换u1在2的补码机器上是无操作的,但在其他机器上需要一些计算。
\n\n\n如果
\nunsigned int通过 an 读取 anint*,则该值必须在 的范围内int,否则会发生整数溢出且行为未定义。它是否正确?
不完全是。位模式必须表示新类型中的有效值,旧值是否存在并不重要是否可表示并不重要。来自 C11 (n1570) [省略脚注]:
\n\n\n\n\n6.2.6.2 整数类型
\n\n对于除 unsigned char 之外的无符号整数类型,对象表示的位应分为两组:值位和填充位(后者不需要任何)。如果有N 个值位,则每个位应表示1到2 N-1之间的2的不同幂,以便该类型的对象应能够表示从0到2 N -1的值使用纯二进制表示这应称为值表示。任何填充位的值均未指定。
\n\n对于有符号整数类型,对象表示的位应分为三组:值位、填充位和符号位。不需要任何填充位;
\n\nsigned char不得有任何填充位。必须只有一个符号位。作为值位的每个位应与相应无符号类型的对象表示中的相同位具有相同的值(如果有符号类型中有M 个值位,无符号类型中有N 个值位,则M\xe2\x89\ 4N)。如果符号位为零,则不会影响结果值。如果符号位为1,则应通过以下方式之一修改该值:\n
\n\n- 符号位为 0 的对应值取反(符号和大小);
\n- 符号位的值为-2 M(二进制补码);
\n- 符号位的值为-2 M -1(补码)。
\n其中哪一个适用是实现定义的,就像符号位为 1 且所有值位为零(对于前两个)的值,还是符号位和所有值位为 1(对于补码)的值是否是陷阱表示或正常值。在符号和大小以及补码的情况下,如果这种表示是正常值,则称为负零。
\n
例如, anunsigned int可以有值位,其中相应的有符号类型 ( int) 有一个填充位,类似于unsigned u = ...; int n = *(int *)&u;可能会导致此类系统上的陷阱表示(其读取是未定义的行为),但反之则不然。
\n\n\n\n
int如果该值在和的范围内unsigned int,则通过任一类型的指针访问它都是完全定义的并给出相同的值。它是否正确?
我认为,标准将允许其中一种类型具有填充位,该填充位始终被忽略(因此,两个不同的位模式可以表示相同的值,并且该位可以在初始化时设置),但始终是陷阱- if-set 位用于其他类型。然而,这种余地至少受到同上的限制。p5:
\n\n\n\n\n任何填充位的值均未指定。符号位为零的有符号整数类型的有效(非陷阱)对象表示形式是相应无符号类型的有效对象表示形式,并且应表示相同的值。对于任何整数类型,所有位都为零的对象表示应是该类型中值零的表示。
\n
\n\n\n在系统上哪里
\nint和long具有相同的范围、对齐方式等,可以int*和long*别名吗?(我想不会。)
当然可以,如果您不使用它们的话;)但是不行,以下内容在此类平台上无效:
\n\nint n = 42;\nlong l = *(long *)&n; // UB\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\n可以
\nchar16_t*加uint_least16_t*别名吗?我怀疑这在 C 和 C++ 之间是不同的。在 C 中,char16_t是 typedefuint_least16_t(正确吗?)。在C++中,char16_t是它自己的原始类型,与uint_least16_t兼容。与 C 不同,C++ 似乎也不例外,允许使用兼容但不同的类型作为别名。
我不确定 C++,但至少对于 C,char16_t是一个 typedef,但不一定对于uint_least16_t,它很可能是某些特定于实现的 typedef __char16_t,某些与uint_least16_t(或任何其他类型)不兼容的类型。