我需要在 C 中做模 256 算术。所以我可以简单地做
unsigned char i;
i++;
代替
int i;
i=(i+1)%256;
我需要在 C 中做模 256 算术。所以我可以简单地做
unsigned char i;
i++;
代替
int i;
i=(i+1)%256;
不,没有什么可以保证unsigned char
有八位。使用uint8_t
from <stdint.h>
,你会很好的。这需要一个支持stdint.h
:任何符合 C99 的编译器都支持的实现,但旧的编译器可能不提供它。
注意:无符号算术永远不会溢出,并且表现为“模 2^n”。有符号算术溢出,行为未定义。
是的,您的两个示例的行为都是相同的。见C99 6.2.5 §9:
涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大一的数字为模减少。
很可能是的,但在这种情况下,其原因实际上相当复杂。
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 == 255
, 和UCHAR_MAX == 255
, 那么加法的结果是256
, 并且是 (signed) 类型int
。
赋值隐式地将值256
从int
back 转换为unsigned char
. 转换为无符号类型是明确定义的;结果是减少模MAX+1
,其中MAX
是目标无符号类型的最大值。
如果i
被声明为unsigned int
:
unsigned int i = UINT_MAX;
i++;
不会有类型转换,但+
无符号类型的运算符的语义也指定了归约模块MAX+1
。
请记住,分配给的值在i
数学上等同于(i+1) % UCHAR_MAX
。UCHAR_MAX
通常是 255
,并且保证至少 255
是,但在法律上它可以更大。
可能有一个奇异的系统UCHAR_MAX
也将存储在签名int
对象中。这将需要UCHAR_MAX > INT_MAX
,这意味着系统必须至少有16 位字节。在这样的系统上,晋升将是从unsigned char
到unsigned int
。最终的结果将是相同的。您不太可能遇到这样的系统。我认为对于某些字节大于 8 位的DSP有 C 实现。字节中的位数由 指定CHAR_BIT
,在 中定义<limits.h>
。
CHAR_BIT > 8
并不一定意味着UCHAR_MAX > INT_MAX
。例如,您可以拥有16 位字节和 32 位CHAR_BIT == 16
s )。sizeof (int) == 2
int
unsigned char c = UCHAR_MAX;
c++;
基本上是的,没有溢出,但不是因为c
是无符号类型。c
这里有一个to的隐藏提升和一个toint
的整数转换,它是完美定义的。int
unsigned char
例如,
signed char c = SCHAR_MAX;
c++;
也不是未定义的行为,因为它实际上等同于:
c = (int) c + 1;
并且从int
to的转换signed char
在此处由实现定义(参见 c99, 6.3.1.3p3 关于整数转换)。CHAR_BIT == 8
假设简化。
有关上述示例的更多信息,我建议阅读这篇文章:
《来自地狱的小 C 函数》
如果您不想使用另一种数据类型,还有另一种未提及的替代方法。
unsigned int i;
// ...
i = (i+1) & 0xFF; // 0xFF == 255
这是有效的,因为模元素 == 2^n
,这意味着范围将是[0, 2^n-1]
,因此位掩码很容易将值保持在您想要的范围内。这种方法可能不会比unsigned char
/uint8_t
版本效率高或低,这取决于你的编译器在幕后做了什么魔法以及目标系统如何处理非字加载(例如,一些 RISC 架构需要额外的操作来加载非字长值)。这还假设您的编译器不会检测到对无符号值使用二次幂模运算并为您替换位掩码,当然,在这种情况下,模使用将具有更大的语义值(尽管使用当然,您的决定的基础并不完全是可移植的)。
这种方法的一个优点是您可以将它用于不是数据类型大小的 2 的幂,例如
i = (i+1) & 0x1FF; // i %= 512
i = (i+1) & 0x3FF; // i %= 1024
// etc.
这应该可以正常工作,因为它应该只是溢出回 0。正如在对不同答案的评论中指出的那样,您应该只在值未签名时执行此操作,因为您可能会得到带有符号值的未定义行为。
然而,最好使用模数保留它,因为维护代码的其他人会更好地理解代码,并且智能编译器可能无论如何都会进行这种优化,这可能首先使其毫无意义。此外,性能差异可能非常小,以至于一开始就无关紧要。
如果您用来表示数字的位数等于除数 -1 的二进制(无符号)表示(100000000)中的位数,在这种情况下为:9-1= 8(字符)