当前位置: 首页 > news >正文

C++ 中有符号数与无符号数的隐式转换与运算陷阱

尽管我们不会故意给无符号对象赋一个负值,却可能写出这样的代码。例如,当一个算术表达式既有无符号数又有int值时,那个int值就会转换成无符号数。把int转换成无符号数的过程和把int直接赋给无符号变量一样:

#include <iostream>int main() {unsigned u = 10, u2 = 42;std::cout << "u2 - u  =" << u2 - u << std::endl;std::cout << "u  - u2 =" << u - u2 << std::endl;int i = 10, i2 = 42;std::cout << "i2 - i  =" << i2 - i << std::endl;std::cout << "i  - i2 =" << i - i2 << std::endl;u = 42;i = 10;std::cout << "i  - u  =" << i - u << std::endl;std::cout << "u  - i  =" << u - i << std::endl;u = 10;i = -42;std::cout << "i  - i  =" << i + i << std::endl;  // prints -84std::cout << "u  - i  =" << u + i << std::endl;  // if 32-bit ints, prints 4294967264i = 10;std::cout << "good" << std::endl;while (i >= 0) {std::cout << i << " ";--i;}std::cout << std::endl;for (int i = 10; i >= 0; --i)std::cout << i << " ";std::cout << std::endl;for (unsigned u = 0; u <= 10; ++u)std::cout << u << " ";  // prints 0 . . . 10std::cout << std::endl;/* NOTE: the condition in the following loopwill run indefinitely// WRONG: u can never be less than 0; the condition will always succeedfor (unsigned u = 10; u >= 0; --u)std::cout << u << std::endl;
*/u = 11; // start the loop one past the first element we want to printwhile (u > 0) {--u;        // decrement first, so that the last iteration will print 0std::cout << u << " ";}std::cout << std::endl;// be wary of comparing ints and unsignedu = 10;i = -42;if (i < u)               // false: i is converted to unsignedstd::cout << i << std::endl;elsestd::cout << u << std::endl;   // prints 10u = 42;u2 = 10;std::cout << "u  - u2  =" << u - u2 << std::endl; // ok: result is 32std::cout << "u2 - u   =" << u2 - u << std::endl; // ok: but the result will wrap around
}

输出:

u2 - u  =32
u  - u2 =4294967264
i2 - i  =32
i  - i2 =-32
i  - u  =4294967264
u  - i  =32
i  - i  =-84
u  - i  =4294967264
good
10 9 8 7 6 5 4 3 2 1 0 
10 9 8 7 6 5 4 3 2 1 0 
0 1 2 3 4 5 6 7 8 9 10 
10 9 8 7 6 5 4 3 2 1 0 
10
u  - u2  =32
u2 - u   =4294967264

有符号数与无符号数的转换规则

  1. 隐式转换:当有符号数与无符号数一起使用时,有符号数会被隐式转换为无符号数。这可能导致负数转换为一个很大的正数。

    int a = -1;
    unsigned b = 1;
    if (a < b) {// 实际上a被转换为无符号数,变成了一个很大的正数
    }
    
  2. 显式转换:可以使用类型转换运算符显式地将一个有符号数转换为无符号数,或反之。需要注意的是,这样的转换可能会导致意料之外的结果。

    int a = -42;
    unsigned b = static_cast<unsigned>(a); // b现在是一个很大的正数
    

无符号数的特性

  1. 零或正数:无符号数总是表示零或正数,没有负数。
  2. 溢出行为:当无符号数的值超出其表示范围时,它会绕回到零。例如,假设unsigned int的最大值为4294967295,如果再加1,就会变成0。
    unsigned int max = 4294967295;
    max += 1; // 结果是0
    

有符号数与无符号数运算中的常见问题

  1. 混合运算:在有符号数和无符号数混合运算时,结果可能不符合预期。

    unsigned u = 10;
    int i = -3;
    std::cout << u + i << std::endl; // i被转换为无符号数,导致结果出乎意料
    
  2. 循环控制:在控制循环的条件中,如果使用无符号数作为循环变量,要特别小心,因为它永远不会小于0。

    for (unsigned u = 10; u >= 0; --u) {std::cout << u << std::endl; // 无限循环
    }
    

C++标准中的相关定义

  1. 整数提升:当执行二元运算时,C++标准要求对操作数进行整数提升。例如,有符号charshort等会被提升为int,无符号的会被提升为unsigned int

    char c = 'a';
    int result = c + 1; // 'a'被提升为int,然后再进行加法运算
    
  2. 算术转换:在进行二元运算时,如果两个操作数类型不同,C++会进行算术转换,将较低等级的类型转换为较高等级的类型。

    unsigned u = 42;
    int i = -42;
    std::cout << i + u << std::endl; // i被转换为无符号数
    

安全使用建议

  1. 尽量避免混合使用:尽量避免将有符号数和无符号数混合使用,以减少潜在的错误。
  2. 检查范围:在进行转换或运算前,检查数值是否在目标类型的表示范围内。
  3. 使用静态分析工具:使用静态分析工具检查代码中的潜在问题,例如混合运算带来的隐患。

相关文章:

  • Android14 WMS-窗口绘制之relayoutWindow流程(二)-Server端
  • Linux操作系统学习:day01
  • leetcode67:二进制求和
  • 享元模式
  • Maven的三种项目打包方式——pom,jar,war的区别
  • 代码随想录刷题笔记-哈希表篇
  • Vue3 渲染函数 API(五)
  • 基于ensp的园区网络搭建综合实验
  • Apollo9.0 PNC源码学习之Control模块(二)
  • 【系统学C++】二、从C语言到C++(二)
  • Java应届第一年规划
  • javaFX为例的MVC案例
  • 宽睿数字平台兼容TDengine 等多种数据库,提供行情解决方案
  • Ansible——stat模块
  • java线程变量共享
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 3.7、@ResponseBody 和 @RestController
  • ComponentOne 2017 V2版本正式发布
  • eclipse(luna)创建web工程
  • JavaScript 基本功--面试宝典
  • javascript数组去重/查找/插入/删除
  • Java方法详解
  • JS变量作用域
  • js面向对象
  • k8s 面向应用开发者的基础命令
  • PHP 小技巧
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • redis学习笔记(三):列表、集合、有序集合
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • SSH 免密登录
  • 订阅Forge Viewer所有的事件
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #数学建模# 线性规划问题的Matlab求解
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (26)4.7 字符函数和字符串函数
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (a /b)*c的值
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (SpringBoot)第二章:Spring创建和使用
  • (ZT)一个美国文科博士的YardLife
  • (二)windows配置JDK环境
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (十三)Flask之特殊装饰器详解
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (转)setTimeout 和 setInterval 的区别