c - 将两个无符号数的减法存储为有符号数的语义

标签 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;
    }

这个减法的结果是-1,看起来只是-1,因为我的电脑是二进制补码。我对么?我正在查看 C11 规范。

如果我们剖析以下语句:

        int32_t c = a - b;

我们根据运算符优先级(如第 76 页注释 85 中所述)开始减法:

a - b

C11 6.5.6/6:

The result of the binary - operator is the difference resulting from the subtraction of the second operand from the first.

这是 -1,因此不适合。转换! C11 6.3.1.3/2:

Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

所以a-b的实际值为4294967295。接下来是赋值运算符:C11 6.5.16.1/2:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

因此无符号值4294967295需要转换为有符号值。规则是什么? C11 6.3.1.3/3:

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

所以这是完全定义的实现,因此最终为 -1,因为这就是我的实现方式。

事实真的是这样吗?如果我在具有补码系统的系统上运行代码,它会产生不同的值吗?或者我忽略了什么?

最佳答案

The result of this subtraction is -1, and it seems like it is only -1 because my computer is two's complement. Am I correct?

在赋值时进行左值转换之前,实际运算a - b的结果为4294967295 = 2^32 - 1 = UINT_MAX,因为减法是在 uint32_t 操作数上执行的,并且根据您引用的 6.3.1.3/2 规则,回绕是明确定义的。无论您的系统使用哪种签名格式,您都将获得 4294967295

Is this really the case?

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

Would the code result in a different value if I run it on a system with one's complement system?

是的。该原始二进制数为 0xFFFFFFFF,因此 impl.define 到有符号的转换很可能将其转换为该原始二进制数的符号格式的相应表示形式:

  • 在 2 的补码系统中,0xFFFFFFFF 给出 -1
  • 在 1 的补码系统上,它会给出 -0,这可能是一个陷阱表示。
  • 在有符号星等系统上,它将给出 -2147483647

不允许使用其他形式(C17 6.2.6.2/2)。

Or am I overlooking something?

一个小细节。与 C 的整数转换规则不同,int32_t 类型是无垃圾的。 保证 (7.20.1.1) 始终使用 2 的补码且无填充位。具有 1 的复杂的奇异符号系统。或带符号的幅度将不容易支持 int32_t - 它实际上是一个可选类型,仅对于 2 的补码实现是强制的。因此,如果不支持 2 的补码,您的代码将无法编译。

另请注意,严格来说,int32_t 的正确格式说明符是 "%"PRId32 而不是 %d

关于c - 将两个无符号数的减法存储为有符号数的语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60828771/

相关文章:

c - FFMPEG 用于从 yuv420p 转换为 rgb bmp 的所有操作是什么?

c - #define 的数组格式(C 预处理器)

c++ - C++ 模板中的罗素悖论

c - 不同对齐的指针UB之间是memcpy吗?

c - C 中的哪些对象声明导致存储被保留(即定义)?

c - C11 _Atomic 说明符与限定符语法不规则的基本原理?

c - 如果 C11 关键字 '_Atomic' 后跟空格和左括号,是否算作类型限定符或说明符?

c - C语言中的malloc语法解释

c - 如何使用套接字在服务器和客户端之间进行通信? [C]

c++ - C++14 中标准布局类的定义