我需要在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
有八位。使用来自 <stdint.h>
的 uint8_t
就可以了,这样您就完全没问题了。这需要支持 stdint.h
的实现:任何符合 C99 标准的编译器都支持,但旧编译器可能不提供此项功能。
注意:无符号算术永远不会溢出,并以“模 2^n”的方式运行。有符号算术溢出会产生未定义的行为。
char
的大小。 - Alexandre C.intN_t
和uintN_t
类型都是可选的,并不能保证一定可用。在任何没有8位字节(尽管这可能不太常见)的系统上,它们很可能不可用。 - Crowmansizeof(char)== sizeof(short)== sizef(int)== 1 == 16位
。在这里建议的假设在那里行不通。 - Jason R是的,你举的两个例子的行为是相同的。请看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
,并且是有符号的 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
。最终结果将是相同的。你不太可能遇到这样的系统。我认为有一些 DSPs 的 C 实现具有大于8位的字节。字节中的位数由 <limits.h>
中定义的 CHAR_BIT
指定。
CHAR_BIT > 8
不一定意味着 UCHAR_MAX > INT_MAX
。例如,您可以有 CHAR_BIT == 16
和 sizeof (int) == 2
,即16位字节和32位的 int
。i++
相当于在递增 i
之前复制 i
并在后缀表达式中使用旧副本。而 ++i
则是真正等价于 i = i + 1
/i += 1
。 - JABunsigned char c = UCHAR_MAX;
c++;
基本上是的,没有溢出,但不是因为c
是无符号类型。这里有一个隐藏的c
转换为int
和从int
到unsigned char
的整数转换,它是完全定义好的。
例如,
signed char c = SCHAR_MAX;
c++;
这也不属于未定义行为,因为实际上它等同于:
c = (int) c + 1;
在这里,从int
到signed char
的转换是实现定义的(请参见c99中的6.3.1.3p3关于整数转换的内容)。为了简化起见,假设CHAR_BIT == 8
。
有关上述示例的更多信息,请阅读此文章:
“来自地狱的小C函数”
unsigned char
值的和不会溢出,但在sizeof(int)
为2的机器上,两个unsigned char
值的乘积可能会溢出。历史上,标准允许16位机器上的编译器对unsigned char x=255; x*=255;
做任何喜欢的事情,并没有被视为缺陷,因为实际上,即使使用16位int
的编译器也会表现得明智。这样的代码产生未定义行为被视为理论问题,而不是编译器否定时间和因果律的机会。 - supercatunsigned 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.
stdint.h
,你可以简单地使用uintXX_t
。http://www.cplusplus.com/reference/cstdint/ - user2485710%
是余数而不是模运算!尽管对于正数来说模运算等同于取余。这两段代码并不等价,因为第一段给出的是模运算结果,而第二段给出的是余数(你的i
是int
而不是unsigned int
)。 - Grijesh Chauhan%256
的技巧,所以你不必微调优化。写清晰的代码有很多值得说的地方 - 特别是如果它是实验并且需要评分!感谢你开启了一个有趣的讨论。 - Floris