C++的那些坑:基本类型。

前言

本文主要介绍自己在使用C++编程时曾遇见的一些bug以及解决方案。

记得刚接触编程时,老师说过:学一门编程语言,要多写代码,多看报错,多dug。的确,对于初学者最忌纸上谈兵,光说不做,看看就行;结果一到亲自写代码,一个 hello world 程序,IDE全是红线 (不知多少人是怎么过来的) 。那时候,最反感的就是莫名巧妙的报错 + 一堆看不懂的英文。当学完基础语法后,刚被语法错误折磨完,又来了逻辑错误。

曾经找个bug,一个钟头这么就过去了 (我那宝贵的游戏时间就这么没了-_-)… …

下面我将介绍一些曾让我受罪的bug:

1. 无符号整型回绕

	vector<int> nums{0, 1, 2};
	for (auto i = nums.size() - 1; i >= 0; i--) 
	    cout << nums[i] << " ";

乍一看好像就只是倒序输出 vector 中的内容,但一运行,会输出不止3个数,进入死循环,这是怎么回事呢?相信知道的小伙伴一眼就能看到问题所在——“auto”。

auto 关键词是C++11引入的,用于自动推导类型

vector的 size() 函数的返回类型为 size_t,查看其定义可知 size_tunsigned long long (不同平台可能不一样,但是都是无符号整型)。我们知道无符号整型有一个回绕问题(简单说就是对于一个无符号整数 i = 0,那么 i - 1 不为 -1,而应该是此类型的最大值。不清楚原因的请自行百度),那么现在再看代码,当 i 减为 0 时,再减 1,就会变为 size_t 的最大值(反正是个正数),而判断条件为 i >= 0,显然会永远满足,从而进入死循环,同时 vector 访问越界。

所以解决方案很简单,只要将 i 设为有符号整型即可。从这个案例也可以看出,当无符号整型参与运算时,我们不得不注意其回绕问题

2. 整除

	int x = 5, y = 2;
	double d = x / y;
	cout << d << endl;

看此代码,如果你不清楚C++的 / 的特性,可能你会觉得 d = 2.5,然而事实上 d = 2。C++ 的 / 并不是传统数学中的除法,其的行为会根据操作数的类型有所不同:

  • 当操作数整型时,/ 表示整除,向下取整,因此返回结果也是 整型。
  • 当操作数存在浮点数时,/ 就与数学中的除法一样,返回的是 浮点型。

因此,只需要将其中一个操作数转为浮点数即可。

插一句,比如以下代码:
double d = 5 / 2;
结果也为 2,原因同上。记得以前上课老师让我们将其改正确,结果诞生出了一堆复杂化代码,显式转换类型,还记得我写的是:
double d = static_cast<double>(5) / 2;
结果最简单的代码是
double d = 5.0 / 2;
当时的内心:我是XX
因此上面给的演示代码可以写为
double d = x * 1.0 / y;

3. 数据溢出

    int x = INT_MAX, y = INT_MAX;
    int res = (x + y) / 2;
    cout << res;

INT_MAX在 <climits> 头文件中 ,表示 int 类型所能表示的最大值 2147483647

如果你不假思索,可能直接认为 res = 2147483647 (INT_MAX) ,但是事实往往事与愿违——res = -1。不讨论结果为什么为 -1,讨论为什么结果不正确:

  • 我将上述的文件编译为汇编,值得注意的内容如下:

在这里插入图片描述

可以看到使用的是 x + y 被送入 32 位的寄存器 edx

因为 x,y 为 int 类型,int 类型占 4 字节,也就是 32 位

  • 现在我将上述代码中的 x, y 的类型改为 long long,然后编译为汇编,内容如下:

在这里插入图片描述
现在 x + y 被送入 64 位的寄存器 rdx

long long 为 8 字节,也就是 64 位

如果你懂汇编,你应该能分析出问题所在
如果你不懂汇编,你可以看下面的不严谨的分析

因此不难分析出问题所在,当 x 与 y 都是 int 时,那么 int res = (x + y) / 2; 可以看做两步指令:

  • int temp = x + y;
  • int res = temp / 2;

temp 为 int 的原因不是 res 是 int,而是因为 x, y 是 int。因此如果你将代码改为 long long res = (x + y) / 2; 结果仍然错误。 (long long res = (x + y) / 2; 可以看做 int temp = x + y; long long res = temp / 2; )

由于第一步指令 x 与 y 的值为 INT_MAX,显然会造成 int 数据溢出,故最后结果不正确。

因此避免此类情况的方法就是采用更大的类型来存储 x, y,或者更好的方法:

将括号展开:int res = x / 2 + y / 2;
又或者: int res = (x - y) / 2 + y;

此 bug 的案例可能比较容易看出问题所在,因为直接告诉你 x,y 很大,但是日常写代码中,很少出现此情况,更多地可能会是一个数在经过多次计算变得较大但是没有溢出,然后经过类似上述的案例,从而发生数据溢出。

  • 26
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值