当前位置: 首页 > 知识库问答 >
问题:

c:在计算中使用无符号操作数时,如何最好地处理无符号整数模运算(“回绕”)

叶琦
2023-03-14

这个范围检查函数需要两个带符号的整数参数:

  range_limit(long int lower, long int upper)

它是用range_limit(0,controller_limit)调用的。我需要将范围检查扩展到包括负数,直到“controller_limit”大小。

我天真的把电话改成了

  range_limit(-controller_limit, controller_limit)

虽然它编译时没有警告,但这并没有像我预期的那样工作。

我忘记了< code>controller_limit是无符号整数。

在C中,简单的整数计算可以产生令人惊讶的结果。例如,这些计算

 0u - 1;

或更相关

 unsigned int ui = 1;
 -ui;

产生无符号整数类型的4294967295(又名UINT_MAX)。据我所知,这是由于整数转换规则和无符号操作数的模运算。

根据定义,无符号算术不会溢出,而是“环绕”。此行为定义良好,因此如果使用以下表达式调用函数,编译器将不会发出警告(至少不会发出gcc):

#include  <stdio.h>

void f_l(long int li) {

    printf("%li\n", li); // outputs: 4294967295

}

int main(void)
{
    unsigned int ui = 1;
    f_l(-ui);
    return 0;
}

自己试试这个代码!

所以我没有传递负值,而是传递了一个高得离谱的正值给函数。

我的修复是从无符号整数转换为int:

    range_limit(-(int)controller_limit, controller_limit);

显然,整数模行为与整数转换规则相结合,允许出现难以发现的细微错误,因为编译器不会帮助发现这些错误。

由于编译器不会发出任何警告,并且您随时可能遇到此类计算,我想知道:

如果必须处理无符号操作数,如何最好地避免无符号整数模算术陷阱?

注意:
虽然gcc在检测整数模算术方面没有任何帮助(在撰写本文时),但clang确实如此。编译器标志“-fsanitize=无符号整数溢出”将启用对模算术的检测(使用“-Wconversion”是不够的),但是,不是在编译时,而是在运行时。自己试试吧!

延伸阅读:< br > Seacord:C和C中的安全编码,第5章,整数安全性

共有1个答案

范承志
2023-03-14

使用有符号整数根本不会改变这种情况。

C实现没有义务引发运行时警告或错误作为对未定义行为的响应。未定义的行为是未定义的,正如它所说;C标准绝对没有提供关于结果的要求或指导。特定的实现可以选择它认为合适的任何机制来响应未定义的行为,包括显式定义结果。(如果你依赖于这个显式的定义,你的程序就不能移植到其他具有不同或未记录行为的编译器上。也许你不在乎。)

例如,GCC在其手册的“实现定义的行为”一节中定义了越界整数转换和一些位运算的结果。

如果您担心整数溢出(并且很多时候您应该担心它),那么由您来保护自己。

例如,而不是允许:

unsigned_counter += 5;

要溢出,您可以编写:

if (unsigned_count > UINT_MAX - 5) {
    /* Handle the error */
}
else { 
    unsigned_counter += 5;
}

在整数溢出会给您带来麻烦的情况下,您应该这样做。一个可能(并且已经!)导致缓冲区溢出漏洞的常见示例来自检查缓冲区是否有足够的空间进行添加:

if (buffer_length + added_length >= buffer_capacity) {
  /* Reallocate buffer or fail*/
}
memcpy(buffer + buffer_length, add_characters, added_length);
buffer_length += added_length;
buffer[buffer_length] = 0; 

如果buffer_length added_length溢出 - 在有符号或无符号的算术中 - 必要的重新分配(或失败)不会触发,memcpy将覆盖内存或segfault或执行您意想不到的其他事情。

这很容易修复,所以值得养成这个习惯:

if (added_length >= buffer_capacity
    || buffer_length >= buffer_capacity - added_length) {
  /* Reallocate buffer or fail*/
}
memcpy(buffer + buffer_length, add_characters, added_length);
buffer_length += added_length;
buffer[buffer_length] = 0;

另一个类似的情况是,当你使用一个循环并且你的增量不止一个时,你会陷入严重的麻烦。

这是安全的:

for (i = 0; i < limit; ++i) ...

这可能导致无限循环:

for (i = 0; i < limit; i += 2) ...

第一个是安全的——假设ilimit是同一类型——因为 i不能溢出

如果您使用的是 GCC,并且不关心完全的可移植性,则可以使用 GCC 溢出检测内置函数来帮助您。它们也记录在GCC手册中。

 类似资料:
  • 我正在简单的C程序中试验无符号int数据类型和主方法参数。作为一个实验,我写了一个程序,从命令行获取一个int数作为main方法的参数,并对该数和0之间的每个整数求和。 例如,程序计算 f(n) = (1 2 3... n) 当 n 时有效 我开始注意到的第一件事是当f(n) 我手动发现数学上的最大值,我的程序生成的结果将是有效的(例如,在整数溢出之前),对于有符号整数为65535,对于无符号in

  • 我在看的书:CS-app 2。c有无符号和有符号的int类型,并且在大多数架构中使用二进制补码算法来实现有符号值;但是学了一些汇编代码之后,发现很少有指令区分无符号和有符号。所以我的问题是: > 区分有符号和无符号是编译器的责任吗?如果是,它是如何做到的? 谁实现两个补码算法——CPU还是编译器? 添加更多信息: 在学习了更多的指令后,实际上有一些指令区分有符号和无符号,例如setg、seta等。

  • 本文向大家介绍C语言中无符号数和有符号数之间的运算,包括了C语言中无符号数和有符号数之间的运算的使用技巧和注意事项,需要的朋友参考一下 C语言中有符号数和无符号数进行运算(包括逻辑运算和算术运算)默认会将有符号数看成无符号数进行运算,其中算术运算默认返回无符号数,逻辑运算当然是返回0或1了。 unsigned int和int进行运算 直接看例子来说明问题吧 输出结果为: 这是因为a和b进行比较的时

  • 有符号和无符号变量在按位运算上有区别吗?< br >例如,在处理无符号数字时:< br> 将得到00000101。 但当处理带符号的数字时会发生什么?

  • 我正在读一篇关于整数安全性的文章。以下是链接:http://ptgmedia.pearsoncmg.com/images/0321335724/samplechapter/seacord_ch05.pdf 在第166页,有这样一句话: 涉及无符号操作数的计算永远不会过流,因为不能由结果无符号整数类型表示的结果将被模化为比结果类型可以表示的最大值大一的数字。 这是什么意思?感谢您的回复。

  • C++ 运算符 使用逗号运算符的为了把几个表达式串在一起。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。从本质上讲,逗号的作用是导致一系列运算被顺序执行。 最右边的那个表达式的值将作为整个逗号表达式的值,其他表达式的值会被丢弃。例如: var = (count=19, incr=10, count+1); 在这里,首先把 count 赋值为 19,把 incr 赋值为 10,然后