C 中的无符号溢出

发布于 2024-09-26 21:59:57 字数 595 浏览 8 评论 0原文

考虑以下 C 代码片段:

#include <stdint.h>

uint32_t inc(uint16_t x) {
 return x+1;
}

在纯 x86_64 系统上使用带有标志 -std=c99 -march=core2 -msse4.1 -O2 -pipe -Wall 的 gcc-4.4.3 编译时,它会产生

movzwl %di,%eax
inc    %eax
retq

现在,预测无符号溢出我对 x86_64 汇编不太了解,但据我所知,16 位参数寄存器正在被移动到 32 位寄存器,该寄存器会递增并返回。我的问题是,如果 x == UINT16_MAX 怎么办?会发生溢出,标准规定 x+1==0,对吗?但是,鉴于 %eax 是一个 32 位寄存器,它现在包含 UINT16_MAX+1,这是不正确的。

这让我在这里连接一个问题:是否有一种可移植的方法来禁用 C 中的无符号溢出,以便编译器可以假设存储在大寄存器中的小变量的高位将始终为 0(因此不需要清除它们)?如果没有(或者如果解决方案在语法上很糟糕),至少在 GCC 中是否有办法做到这一点?

非常感谢您抽出时间。

Consider the following piece of C code:

#include <stdint.h>

uint32_t inc(uint16_t x) {
 return x+1;
}

When compiled with gcc-4.4.3 with flags -std=c99 -march=core2 -msse4.1 -O2 -pipe -Wall on a pure x86_64 system, it produces

movzwl %di,%eax
inc    %eax
retq

Now, unsigned overflow is predicted in C. I do not know much about x86_64 assembly, but as far as I can see the 16bit argument register is being moved to a 32bit register, which is incremented and returned. My question is, what if x == UINT16_MAX. An overflow would occur and the standard dictates x+1==0, right? However, given %eax is a 32bit register, it now contains UINT16_MAX+1, which is not correct.

This lets me connect one question here: is there a portable way to disable unsigned overflow in C so that the compiler can assume the upper bits of a small variable stored in a large register will always be 0 (so it needs not clear them)? If not (or if the solution is syntactically nasty), is there a way to do it at least in GCC?

Thank you very much for your time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

就是爱搞怪 2024-10-03 21:59:57

不可以,C 类型属于默认促销活动。假设uint16_t的转换等级低于int,它将被提升为int并且加法将作为int<进行/code>,然后在返回时转换为 uint32_t

至于你最后提出的相关问题,我不太明白你想要什么。

No, C types are subject to default promotions. Assuming uint16_t has lower conversion rank than int, it will be promoted to int and the addition will be carried out as an int, then converted to uint32_t when returned.

As for your related question at the end, I don't quite follow what you want.

耀眼的星火 2024-10-03 21:59:57

使用不使用编译器中介进行计算的编码风格,请注意 (1) 将具有数据类型 int

uint32_t inc(uint16_t x) {
 uint16_t y = x + 1;
 return y;
}

Use a coding style that does not use compiler intermediaries for calculations, note that (1) is going to have the data type int.

uint32_t inc(uint16_t x) {
 uint16_t y = x + 1;
 return y;
}
冷情妓 2024-10-03 21:59:57

该标准描述整数溢出的方式的一个特点是它允许编译器假设不会发生溢出。在您展示的情况下,编译器不会保留溢出的行为,因为毕竟 x+1 可能采用的可能值的范围(假设不存在溢出)适合返回类型。

A peculiarity of the way the standard describes integer overflow is that it allows compilers to assume that an overflow cannot occur. In the case you show there, the compiler is not expected to preserve the behavior of an overflow, since after all, the range of possible values that x+1 may take (assuming that overflow doesn't exist) fit in the return type.

如痴如狂 2024-10-03 21:59:57

对于你的第二个问题,在C中,不存在无符号类型的溢出之类的事情,适用的术语是包装。根据定义,无符号类型以 2^width 为模进行计算。每当您将较宽的无符号类型转换为较窄的无符号类型时,高位都会被丢弃。所有的C编译器都应该这样实现,你不必担心。

本质上,无符号类型非常简单,令人讨厌的事情只发生在有符号类型上。

For your second question, in C there is no such thing as overflow for unsigned types, the applicable term is wrapping. By definition unsigned types are computed modulo 2^width. Whenever you cast a wider unsigned type to one that is narrower the upper bits will simply be thrown away. All C compilers should implement it like this, there is nothing you have to worry about.

In essence unsigned types are quite simple, the nasty things only come for signed types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文