写代码这么长时间,回答各种 c 或 c++ 的初等问题,我有一个及其深的感受,初学者不会debug。
哪怕是最简单的问题,没有趁手的工具,你都很难解决,尤其是 c 或 c++。
clangd是个趁手的工具,去学它,那是世界上最好的老师,分分钟教你做 c++er。
gdb 或 lldb是趁手的工具,去学它,让 bug 无处躲藏。
我目前的编程环境如题目所示。如果你和我一样的环境,或想用lldb进行debug,可以继续读下去。
clangd + lldb 编程环境的建立,请移步:2022-03-30 VsCode中使用clangd插件的各种设置
OK,那么假设你的环境搭建好了,你要对如下这个简单的程序进行debug:
这是一个简单的程序,输入当前时速speed和限速limit,当速度大于等于1.5倍limit时,吊销驾照,当速度大于等于1.1倍limit时,罚款200.
多么清晰。但是当你输入110 100,也就是时速110,限速100时,提示为OK!
没有收到罚单。警察叔叔放过了你。
这是不可以的,但是为什么?
一眼看去,程序逻辑清晰,基本是小学3年级算数,为啥一台如此高等的计算机就不会做。
#include
using namespace std;
int main()
{
int speed;
int limit;
double x;
cin >> speed >> limit;
if (speed >= limit * 1.5)
{
x = (speed - limit) / limit * 100;
cout << "Exceed " << x << "%.License Revoked" << endl;
}
else if (speed >= limit * 1.1)
{
x = (speed - limit) / limit * 100;
cout << "Exceed " << x << "%.Ticket 200" << endl;
}
else
{
cout << "OK" << endl;
}
return 0;
}
轻轻按下F5,大约收到8个警告,但是编译是成功的。
我们来到终端,输入 110 100 按回车。
然后就出来了。
110 100
OK
E:\clangC++>
说好的debug呢,我们完美的错过了。
为什么完美错过,因为没有断点。为什么没有断点,因为没有打断点。怎么打断点?
在运行和调试界面,断点对话框点击 “ + ” 输入 “ main ”
这是告诉lldb,进入主函数立即停止,等待下一步命令。
你可以在这里填入任何函数!调试会在这个函数下停止,等待命令。
然后在这行代码处按 F9 断点调试框下就出现了此行代码的断点。
else if (speed >= limit * 1.1)
你甚至可以在此行代码的左侧红点处点击鼠标右键,对断点进行编辑,比如输入
speed >= 110
那么当条件满足时,调试程序会在此行代码处停止,等待。
现在,让我们重新按 F5 进入debug,神奇的事情发生了,debug的指示标志停在了我们刚刚设置的main函数处,OK,我们继续,按F10,并在终端输入110 100 回车。
此时指示停在了我们设置的代码处:
else if (speed >= limit * 1.1)
此时,我们查看运行和调试框中的变量:
local
speed:110
limit:100
x:乱七八糟不知是啥
我们用小学数学计算一下,limit * 1.1 = 110 ,此处应有掌声。
但是,为了保险起见,我们还是查看一下,计算机眼里 limit * 1.1 究竟是多少。
我们在运行和调试框的监视中点击 " + " , 输入 limit * 1.1,神奇的事情又发生了!
这个值是:
limit * 1.1: 110.00000000000001
它不是110,重复,它不是110!
OK ,bug已经找到,问题的解决就在这一刻,我们要如何使得这个值等于110呢? 而它为啥不是110呢?
让我们回到 C++ 的环境,看看这句代码究竟是什么意思。
else if (speed >= limit * 1.1)
如果 speed 大于等于 limit 乘以 1.1 则…
这是一个复合语句,先求积,再比较。而乘法的过程是一个 int 乘以一个 double,这里是重头戏,危险的隐式转换。
继而一个 int 又和一个double 比较大小,又进行隐式转换。
而且我们哪里知道,100 * 1.1 不等于 110。
是的,在计算机眼中,二者不等。
不知你的眼里是否湿润了,反正我被惊到了。比较两个double要小心,比较两个float更要小心。在浮点的语境里,只有精度值。
问题找到了,怎么办?
既然是隐式转换搞出的问题,就强制转换解决,double不能比较,就用 int。
static_cast 了解一下:
if (speed >= static_cast<int>(limit * 1.5))
修复了这个代码,重新debug,终于不在是超速OK了。
但是超速变成 0 了,仍然罚款了。
110 100
Exceed 0%.Ticket 200
剩下的问题,就看你的了,程序是有限的,bug是无限的,以有涯随无涯,殆已。