C 将两个无符号相减存储为一个有符号符号的语义

C 将两个无符号相减存储为一个有符号符号的语义,c,language-lawyer,c11,C,Language Lawyer,C11,我将减法运算为无符号数,并将其存储为有符号类型,这样就行了。我不完全理解这为什么会起作用。以以下为例: #include <stdio.h> int main(void) { uint32_t a = 1; uint32_t b = 2; int32_t c = a - b; printf("%"PRId32"\n", c); return 0; } 根据运算符优先级(

我将减法运算为无符号数,并将其存储为有符号类型,这样就行了。我不完全理解这为什么会起作用。以以下为例:

    #include <stdio.h>

    int main(void)
    {
        uint32_t a = 1;
        uint32_t b = 2;
        int32_t c = a - b;
        printf("%"PRId32"\n", c);
        return 0;
    }
根据运算符优先级(如第76页注释85所述),我们从减法开始:

a - b
C116.5.6/6:

二进制运算符的结果是由 从第一个操作数减去第二个操作数

这是-1,因此不适合。转化! C116.3.1.3/2:

否则,如果新类型是无符号的,则该值将由 重复地加上或减去一个大于最大值 可以在新类型中表示,直到值在 新型的

因此
a-b
的实际值是
4294967295
。接下来是赋值运算符:C116.5.16.1/2:

在简单赋值(=)中,转换右操作数的值 并替换存储的值 在由左操作数指定的对象中

因此,无符号值
4294967295
需要转换为有符号值。规则是什么? C116.3.1.3/3:

否则,将对新类型进行签名,并且无法表示该值 在它里面;结果要么是定义了实现,要么是 引发实现定义的信号

这是完全定义的实现,因此最终为-1,因为这就是我的实现

真的是这样吗?如果我在一个有补码系统的系统上运行代码,会产生不同的值吗?还是我忽略了什么

这个减法的结果是-1,看起来只有-1,因为我的电脑是2的补码。我说得对吗

在赋值时进行左值转换之前,实际操作
a-b
的结果是
4294967295
=
2^32-1
=
UINT\u MAX
,因为减法是在
uint32\u t
操作数上执行的,根据您引用的6.3.1.3/2规则,换行是定义良好的。无论系统使用哪种签名格式,您都将获得
4294967295

真的是这样吗

是的,您已经正确阅读并引用了标准

如果我在一个有补码系统的系统上运行代码,会产生不同的值吗

对。这是一个原始二进制数,它是
0xFFFFFFFF
,因此简单定义的有符号转换很可能将其转换为该原始二进制数的有符号格式的对应表示形式:

  • 在2的补码系统中,
    0xFFFFFFFF
    给出
    -1
  • 在1的补码系统中,它将给出
    -0
    ,这可能是一种陷阱表示
  • 在有符号震级系统上,它将给出
    -2147483647
不允许使用其他形式(C17 6.2.6.2/2)

还是我忽略了什么

一个小细节。与C的整数转换规则不同,
int32\u t
类型是无垃圾的。保证(7.20.1.1)始终使用2的补码,不使用填充位。一个奇异的有符号系统。或者,带符号的幅值将不容易支持
int32\t
——它实际上是一种可选类型,仅在2的补码实现中是必需的。因此,如果不支持2的补码,您的代码将无法编译


还请注意,严格地说,
int32_t
的正确格式说明符是
“%”PRId32
,而不是
%d

int32_t
被定义为2的补码(C11 7.20.1.1/1),因此-1将是从4294967295转换为
int32_t
值的最自然的实现,尽管没有什么可以阻止实现执行其他操作。另外,如果
int
可以表示
uint32\u t
的所有值,那么不管怎样,减法的结果都将是-1。@Cacahuetverito否,
a
b
将首先转换为
int
,因为积分提升IIRC。@L.F.哦,真的。不一致太多,无法依赖隐式转换。:)@真的!参见6.5.5/4:如果两个操作数都有算术类型,则对它们执行通常的算术转换。Re“这是完全实现定义的,因此最终为-1,因为这只是二的补码系统中的情况”:这不是有效的推断。转换的结果是由实现定义的,使用二的补码的C实现通常会定义到有符号类型的溢出转换的结果,以有效地使用低位,但它们不是必需的,因此您无法推断。实现可能选择的另一个合理行为是生成溢出信号。
a-b
不一定是
4294967295
。如果
INT\u MAX>=UINT32\u MAX
,则两个操作数都将通过整数提升转换为
INT
,结果将为
-1
。否则,如果
UINT\u MAX>=UINT32\u MAX
,则两个操作数都将通过整数提升转换为
无符号int
,结果将是
UINT\u MAX
(在这种情况下,必然与
UINT32\u MAX
相同)@IanAbbott假设32位数字不是小整数类型,并且
int
不是64位或更大,这是非常安全的。没有这样的系统存在,也不可能永远存在,因为它们的实现质量太低,在实践中毫无用处。我同意,在分配时执行的
uint32_t
int32_t
转换很可能是保留代表性的。然而,就标准而言,当
a - b