【问题标题】:Unsigned Overflow in CC中的无符号溢出
【发布时间】:2011-04-23 04:24:47
【问题描述】:

考虑以下一段 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

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

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

非常感谢您的宝贵时间。

【问题讨论】:

    标签: c assembly overflow int unsigned


    【解决方案1】:

    不,C 类型受默认促销活动的约束。假设uint16_t的转化排名低于int,则将其提升为int,并以int进行加法,返回时转换为uint32_t

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

    【讨论】:

    • @R.: 不是提升为unsigned int吗?
    • @Jens:没有。 uint16_t 的范围适合int,因此将提升为intunsigned char 也一样。
    • 对不起,我没有考虑到这一点。尽管如此,当我将代码更改为“返回 x + (uint16_t)1”时,它会给出相同的结果。我相信提升规则规定两个 uint16_t 的总和给出一个 uint16_t,或者它们给出一个 uint32_t?
    • @Luis: 不,在 C 中根本没有没有int更小的算术。任何算术的结果表达式的类型为 int 或更大。如果int 的转化排名高于uint16_t,则两个uint16_t 变量之和的类型为int,如果int 的转化排名低于uint16_t,则为uint16_t。发生哪一个取决于您的实现中 uint16_t 的特定等级,但有一些规则将类型范围的相对大小与其等级相关联。
    • @R:嗯,对我来说很明显标准不是很连贯,更不用说简单了。 :-) 除此之外,在带有 gcc 的 x86_64 中,sizeof(int)==4 但 sizeof(size_t)==sizeof(void*)==8。因此,问题可以扩展到 uint64_t 和 uint32_t 类型,它们的 rank 都大于或等于 int。这样我们就可以绕过这个,在我看来,奇怪的限制。
    【解决方案2】:

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

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

    【讨论】:

    • 这给出了正确的行为。谢谢。不过,我想要的是一个关于如何让编译器假设不会发生这种溢出的想法,因此它不需要擦除表示 y 的寄存器中的额外位。
    • 您将不得不使用不同的语言,它实际上是 C 语言的一个相当基本的人工制品。使用示例包括用于数字信号处理的伽罗瓦域算法。
    【解决方案3】:

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

    【讨论】:

    • 真实但无关紧要。这个表达式不能溢出。
    【解决方案4】:

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

    本质上,无符号类型非常简单,只有有符号类型才会出现讨厌的事情。

    【讨论】:

    • 正确但无关紧要。此代码不包含有符号或无符号溢出。
    • 其实是相关的。编译器为我做这件事,但它会降低运行时性能。我想禁用它。
    • @R.:确保代码不包含它。我刚刚回答了这个问题,所以它与第二个问题有关。
    • @Luís Fernando Schultz Xavier:我不认为它对性能有影响。只要始终使用正确的无符号类型,所有对您来说似乎很复杂的掩码都会产生简单的汇编指令。如果您真的很担心,请查看它生成的汇编程序,如果您更担心,请对其进行基准测试。
    • @Jens Gustedt:这几乎是我想要避免的。生成的汇编代码随编译器版本、平台和许多其他因素而变化。我正在寻找一种方法来更改对无符号类型的操作的语义。
    猜你喜欢
    • 2020-06-07
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2014-12-09
    相关资源
    最近更新 更多