在C中签名为无符号转换 - 它总是安全吗?
假设我有以下C代码。在C中签名为无符号转换 - 它总是安全吗?
unsigned int u = 1234;
int i = -5678;
unsigned int result = u + i;
什么的隐式转换这里发生了,而且是这个代码安全起见u
和i
所有值? (安全,在这个意义上,即使结果在这个例子中会溢出一些巨大的正数,我可以将它转换回一个INT并获得真正的结果。)
简答
i
你将转换为无符号整数通过添加UINT_MAX + 1
,然后加入将与无符号值进行,导致大result
(取决于u
i
和的值)。
长的答案
按照C99标准:
6.3.1.8常见的算术转换
- 如果两个操作数具有相同的类型,则不需要进一步的转换。
- 否则,如果两个操作数已签署整数类型或两者都具有的无符号整数类型,具有较小整数转换等级的类型的操作数转换为操作数的具有更大的秩的类型。
- 否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,然后用带符号的整数类型的操作数被转换成无符号整数类型的操作数的类型。
- 否则,如果用符号整型操作数的类型,可以表示所有与无符号整数类型的操作数的类型的值,则与无符号整数类型的操作数转换为操作数的类型与符号整数类型。
- 否则,两个操作数都转换为与带符号整数类型的操作数的类型相对应的无符号整数类型。
在你的情况,我们有一个unsigned int类型(u
)和符号int(i
)。参照上面(3),由于两个操作数具有相同的等级,所以你的i
将需要被转换为为无符号整数。
6.3.1.3符号和无符号整数
- 当与整数类型的值被转换为比其它_Bool另一个整数类型,如果该值可以通过新的类型来表示,它是不变的。
- 否则,如果新类型是无符号的,则该值是通过重复地加上或减去小于能够在新的类型来表示,直到该值是在新的类型的范围的最大值一个更转换。
- 否则,新类型被签名并且其值不能被表示;结果是实现定义的或实现定义的信号被引发。
现在我们需要参考上面的(2)。您的i
将通过添加UINT_MAX + 1
转换为无符号值。所以结果将取决于您的实施如何定义UINT_MAX
。这将是大的,但它不会溢出,因为:
6.2.5(9)
涉及无符号的操作数的一种计算可以永远不会溢出,因为不能由所得到的无符号整数表示的结果类型被减少的模数大于可由最终类型表示的最大值的数。
奖励:算术转换半WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
你可以使用这个链接来试试这个在线:http://codepad.org/yPhYCMFO
奖励:算术转换副作用
算术转换规则可用于获取的值0通过初始化一个无符号值到-1
,即:
unsigned int umax = -1; // umax set to UINT_MAX
这保证是便携式无论由于上述的转换规则的系统的符号数表示的。看到这个问题的更多信息:Is it safe to use -1 to set all bits to true?
当一个无符号和一个有符号变量被添加(或任何二进制操作)都被隐式转换为无符号,这在这种情况下会导致巨大的结果。
因此,它的结果可能是巨大的和错误的,但它永远不会崩溃。
从signed到unsigned的转换有两种可能性。最初为正值的数字仍然(或被解释为)相同的值。现在被解释为更大的正数。
正如之前所回答的,您可以在有问题和无问题之间来回转换。有符号整数的边界大小写是-1(0xFFFFFFFF)。尝试添加和减去,你会发现你可以退回并保持正确。
然而,如果你将要铸造来回,我会强烈建议命名变量,使得它清楚自己是什么类型的,如:
int iValue, iResult;
unsigned int uValue, uResult;
这是太容易得到被更重要的问题分心,并忘记哪个变量是什么类型,如果他们没有提示命名。您不希望转换为无符号数,然后将其用作数组索引。
参照the bible:
- 你的加法操作导致要转换为无符号的int INT。
- 假设二进制补码表示和相同大小的类型,位模式不会改变。
- 从unsigned int到signed int的转换依赖于实现。 (但是它可能按照你现在在大多数平台上的预期方式工作。)
- 在结合带符号和无符号大小不同的情况下,规则稍微复杂一些。从
转换符号到无符号不不不一定只是复制或重新解释的符号值的表示。引述C标准(C99 6.3.1.3):
当与整数类型的值被转换为比其它_Bool另一个整数类型,如果 值可以通过新的类型来表示,它是不变的。否则,如果新类型是无符号的,则通过重复添加或将新值类型 中可以表示的最大值减去1,直到该值位于新类型的范围内,该值被转换。
否则,新类型被签名并且其值不能被表示; 结果是实现定义的或者实现定义的信号被引发。
对于近来普遍使用的二进制补码表示,规则确实对应于重新解释这些位。但对于其他表示(符号和大小或补码),C实现必须仍然安排相同的结果,这意味着转换不能只复制位。例如,(无符号)-1 == UINT_MAX,无论表示如何。
通常,C中的转换被定义为对值进行操作,而不是对表示进行操作。
要回答原来的问题:
unsigned int u = 1234;
int i = -5678;
unsigned int result = u + i;
i的值被转换成无符号整型,得到UINT_MAX + 1 - 5678
。然后将该值添加到无符号值1234,产生UINT_MAX + 1 - 4444
。
(不同于无符号溢出,溢出签订调用未定义行为环绕式是常见的,但不是由C标准保证 - 和编译器优化可以在代码,使无根据的假设肆虐。)
可怕的答案嘉豪
Özgür的Ozcitak
当您从符号到无符号 投(和反之亦然)内部 表示的号码不会 更改。 编译器如何解释符号位。
这是完全错误的。
垫弗雷迪克森
当一个无符号和一个签署 变量被添加(或任何二进制 操作)都是隐式 转换为无,这将在 这种情况下结果在一个巨大的结果。
这也是错误的。由于无符号类型中的填充位,无符号整数可以被提升为整数,因为它们具有相等的精度。
SMH
你的加法运算引起INT 转换为一个unsigned int。
错误。也许它确实,也许它没有。
从无符号整数转换为有符号的 int取决于实现。 (但 它可能运作的,你希望 在大多数平台上,这些天的方式。)
错误。如果它导致溢出或值被保留,它可以是未定义的行为。
匿名
i的值被转换为 无符号整型...
错误。取决于int相对于unsigned int的精度。
泰勒价格
正如前面得到的回答是,你可以 投来回 无符号签署没有问题。
错误。试图存储超出有符号整数范围的值会导致未定义的行为。
现在我终于可以回答这个问题。
如果int的精度等于unsigned int,则u将被提升为带符号的int,并且您将从表达式(u + i)中获取值-4444。现在,如果你和我有其他的值,你可能会发生溢出和未定义的行为,但确切的数字,你会得到-4444 [1]。该值将具有类型int。但是你试图将这个值存储到一个无符号整型中,这样就会被转换为一个无符号整数,并且结果的值将会是(UINT_MAX + 1)-4444。
如果无符号的精度int大于int的值,signed int将被提升为一个unsigned int,产生值(UINT_MAX + 1) - 5678,它将被添加到另一个unsigned int 1234.如果你和我有其他的值,表达式落在范围{0..UINT_MAX}之外时,值(UINT_MAX + 1)将被添加或减去,直到结果DOES落在范围{0..UINT_MAX)内且不会发生未定义的行为。
什么是精度?
整数具有填充位,符号位和值位。无符号整数显然没有符号位。无符号字符进一步保证没有填充位。一个整数的值的位数是它的精度。
[陷阱]
宏的sizeof宏不能单独被用来确定一个整数的精度,如果填充比特都存在。并且字节的大小不一定是由C99定义的八位字节(八位)。
[1]溢出可能发生在两点之一。在添加之前(在提升期间) - 当你有一个unsigned int,这个int太大而不能放入int。即使unsigned int在int范围内,加法后溢出也可能发生,加法后结果可能仍然溢出。
在一个不相关的说明,我是一个刚毕业的学生试图找到工作;)
什么隐式转换是怎么回事,
我会被转换为无符号整数。
并且此代码对您和我的所有值都是安全的吗?
在明确定义的意义上是安全的(见https://stackoverflow.com/a/50632/5083516)。
规则的写入通常很难读取标准说话,但基本上在有符号整数中使用任何表示形式,无符号整数将包含数字的2的补码表示形式。
加法,减法和乘法将在这些数字上正确工作,从而产生另一个无符号整数,其中包含表示“真实结果”的二进制补码数字。
除法和转换为较大的无符号整数类型将具有明确定义的结果,但这些结果不会是“实际结果”的2的补码表示。
(安全,即使这个例子中的结果会溢出到一些巨大的正数,我可以将它转换回int并获得真正的结果。)
虽然从转换符号到无符号由标准的反向定义是实现定义的GCC和MSVC定义转换,这样你会得到“真正的结果”时,将储存在2的补数无符号整数回到有符号整数。我希望你只能在晦涩的系统上发现任何其他行为,这些行为不使用2作为有符号整数的补码。
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx
哇那里。它的定义是从签名到未签名,但是从无符号到签名是由实现定义的。 – rlbond 2009-07-18 17:00:34
这是不正确的。从语言的角度来看,从'int'到'unsigned int'的整数转换与源对象的值有关,与其内部表示无关(概念上)。该值使用模2^N算法进行转换,其中N是“unsigned int”中值的位数,无论表示实现用于“int”。 – 2010-07-08 12:03:39
这个答案根本不对。它解释了常见的实现是如何工作的,而不是语言如何工作。 – 2010-08-07 18:18:14