C中溢出后有符号和无符号的不一致相等

Posted

技术标签:

【中文标题】C中溢出后有符号和无符号的不一致相等【英文标题】:inconsistent equality of signed and unsigned after overflow in C 【发布时间】:2016-03-21 23:49:19 【问题描述】:

我正在尝试测试 C 中 short、int 和 long 的有符号和无符号版本的相等性。特别是,我使用了以下测试代码:

#include <stdio.h>

int main() 
    signed short ss = 0x8000; // 2^15
    unsigned short us = 0x8000;
    printf("ss = %i, us = %i\n", ss, us);
    if (ss == us)  printf("ss == us\n"); 
    else  printf("ss != us\n"); 

    signed int si = 0x80000000; // 2^31
    unsigned int ui = 0x80000000;
    printf("si = %i, ui = %i\n", si, ui);
    if (si == ui)  printf("si == ui\n"); 
    else  printf("si != ui\n"); 

    signed long sl = 0x8000000000000000L; // 2^63
    unsigned long ul = 0x8000000000000000UL; // 2^63
    printf("sl = %li, ul = %lu\n", sl, ul);
    if (sl == ul)  printf("si == ui\n"); 
    else  printf("sl != ul\n"); 

代码输出如下:

ss = -32768, us = 32768
ss != us
si = -2147483648, ui = -2147483648
si == ui
sl = -9223372036854775808, ul = 9223372036854775808
si == ui

所以对于短裤来说,它们是不相等的,但对于其他两条来说,它们是相等的。我的假设是否有问题,或者这只是 C 的一个已知怪癖?

【问题讨论】:

这不是怪癖,而是标准行为。 您没有在printf 语句中进行类似的比较:在前两个(但不是第三个示例)中,您对有符号和无符号值都使用%i(有符号整数),它打印被告知放置在堆栈上的数据。一旦到了那里,除了上下文之外,就无法判断它们是签名还是未签名。在short 的情况下,值在放入堆栈之前被提升为signed intunsigned int(因为变量类型),然后由于%i 说明符而打印为signed int 【参考方案1】:

似乎在您的编译器中,shortunsigned short 都可以转换为 int 而不会丢失信息;它们的比较是在转换后完成的(“促销”)。

促销活动在securecoding.cert.org的Usual Arithmetic Conversions部分中进行了解释

    如果两个操作数的类型相同,则不再进行转换 需要。

    如果两个操作数都是相同的整数类型(有符号或 unsigned),具有较小整数转换类型的操作数 rank 转换为具有更高等级的操作数的类型。

    如果 具有无符号整数类型的操作数的秩大于或 等于另一个操作数的类型的等级,操作数与 有符号整数类型转换为操作数的类型 无符号整数类型。

    如果操作数的类型带符号 整数类型可以表示该类型的所有值 无符号整数类型的操作数,无符号的操作数 整数类型转换为带符号的操作数的类型 整数类型。

    否则,两个操作数都将转换为无符号数 与带符号的操作数类型对应的整数类型 整数类型。

【讨论】:

【参考方案2】:

鉴于您的输出,shortunsigned short 小于 int。第一次比较时两者都转换为int,转换后的值不同。

对于intunsigned int,比较执行为unsigned int。转换给出相同的值并且比较是正确的。请注意,您应该使用适当的格式打印数字:printf("si = %i, ui = %u\n", si, ui);

对于longunsigned long 测试,您的计算机似乎有64 位长,比较执行为unsigned longlong 值转换为unsigned long 但具有相同的表示,比较是真的。

请注意,您不应将大于最大值的数字存储到有符号类型中:

signed short ss = 0x8000; 不能保证将-32768 存储到ss

signed int si = 0x80000000;signed long sl = 0x8000000000000000L; 的问题相同。

【讨论】:

以上是关于C中溢出后有符号和无符号的不一致相等的主要内容,如果未能解决你的问题,请参考以下文章

sprintf转换类型和参数类型必须一致么

有符号和无符号整型数据溢出问题

C语言++符号的问题!vc++6.0和Dev-C++运行结果不一致!

函数 write() 与数字符号不一致

js 自动类型转换

C语言 | 每日基础