算术溢出是否等效于模运算?

时间:2014-02-06 18:35:39

标签: c overflow modulo integer-arithmetic

我需要在C中进行模256运算。所以我可以简单地做

unsigned char i;
i++;

而不是

int i;
i=(i+1)%256;

7 个答案:

答案 0 :(得分:23)

没有。没有什么可以保证unsigned char有八位。使用uint8_t中的<stdint.h>,您就可以了。这需要一个支持stdint.h的实现:任何符合C99的编译器都可以,但是较旧的编译器可能不会提供它。

注意:无符号算术永远不会溢出,并且表现为“modulo 2 ^ n”。带有未定义行为的带符号算术溢出。

答案 1 :(得分:6)

是的,两个示例的行为都是一样的。参见 C996.2.5§9:

  

涉及无符号操作数的计算永远不会溢出,   因为无法用结果无符号整数类型表示的结果是   减少模数可以是最大值的数字   由结果类型表示。

答案 2 :(得分:5)

unsigned char c = UCHAR_MAX;
c++;

基本上是的,没有溢出,但不是因为c是无符号类型。这里有cint的隐藏促销,以及从intunsigned char的整数转换,并且已完美定义。

例如,

 signed char c = SCHAR_MAX;
 c++;

也不是未定义的行为,因为它实际上等同于:

c = (int) c + 1;

并且intsigned char的转换是在此实现定义的(有关整数转换的参见c99,6.3.1.3p3)。假设CHAR_BIT == 8为简化。

有关上述示例的更多信息,建议您阅读以下文章:

“来自地狱的小C功能”

http://blog.regehr.org/archives/482

答案 3 :(得分:4)

很可能是的,但在这种情况下它的原因实际上相当复杂。

unsigned char i = 255;
i++;

i++相当于i = i + 1

(好吧,差不多。i++在增加之前产生i 的值,所以它实际上相当于(tmp=i; i = i + 1; tmp)`。但由于在这种情况下结果被丢弃,因此不会产生任何其他问题。)

由于unsigned char是一种较窄的类型,unsigned char运算符的+操作数会提升为int(假设int可以包含所有可能的值unsigned char)的范围。因此,如果i == 255UCHAR_MAX == 255,则添加的结果为256,并且类型为(已签名)int

该作业隐式将<{1}}的值256转换回intunsigned char。转换为无符号类型已明确定义;结果是模MAX+1减少,其中MAX是目标无符号类型的最大值。

如果i被声明为unsigned int

unsigned int i = UINT_MAX;
i++;

没有类型转换,但无符号类型+运算符的语义也指定缩减模块MAX+1

请注意,分配给i的值在数学上等同于(i+1) % UCHAR_MAXUCHAR_MAX 通常是 255,并且保证至少 255,但它可以在法律上更大。

可能存在一个奇异的系统,UCHAR_MAX也可以存储在签名的int对象中。这将需要UCHAR_MAX > INT_MAX,这意味着系统必须具有至少 16位字节。在此类系统上,促销活动将从unsigned char升级到unsigned int。最终结果是一样的。你不太可能遇到这样的系统。我认为有些DSPs的C实现有大于8位的字节。字节中的位数由CHAR_BIT中指定的<limits.h>指定。

CHAR_BIT > 8并不一定意味着UCHAR_MAX > INT_MAX。例如,您可以拥有CHAR_BIT == 16sizeof (int) == 2,即16位字节和32位int s。

答案 4 :(得分:3)

如果您不想使用其他数据类型,还有另一种未提及的替代方法。

unsigned int i;
// ...
i = (i+1) & 0xFF; // 0xFF == 255

这是因为模数元素== 2^n,意味着范围将是[0, 2^n-1],因此位掩码可以轻松地将值保持在您想要的范围内。这种方法可能不会比unsigned char / uint8_t版本的效率高或效率低,这取决于编译器在幕后的作用以及目标系统如何处理非字加载(例如,某些RISC体系结构需要额外的操作来加载非字大小的值)。这也假设您的编译器不会检测对无符号值的二次幂模运算的使用,并为您替换位掩码,当然,就像模数用法具有更大的语义值一样(尽管使用它因为你的决定的基础当然不是完全可移植的。

这种方法的一个优点是你可以将它用于两个不同于数据类型大小的幂,例如

i = (i+1) & 0x1FF; // i %= 512
i = (i+1) & 0x3FF; // i %= 1024
// etc.

答案 5 :(得分:2)

这应该可以正常工作,因为它应该溢出回0.正如在对不同答案的注释中指出的那样,只应在值无符号时执行此操作,因为您可能会获得带有符号值的未定义行为。

最好不要使用模数来保留它,因为维护代码的其他人会更好地理解代码,而智能编译器无论如何都可能正在进行这种优化,这可能使它首先变得毫无意义。此外,性能差异可能很小,一开始就没关系。

答案 6 :(得分:1)

如果用于表示数字的位数等于除数-1的二进制(无符号)表示(100000000)中的位数,它将起作用 在这种情况下是:9-1 = 8(char)