C++ 古法调试:使用 assert 宏

沃尔特·萨维奇的书 《Problem Solving with C++ 》第五章在介绍函数的抽象原则时,提到:函数的注释中应该包括了这个函数被调用的“前提”和“结果”,正确的前提将保证得到正确的结果,否则则是错误的调用。

在写完程序进行调试(确定程序编写无误)的时候,除了调用编译器的调试组件随时查看变量值,还可以检查一个函数的调用前提是否被满足。这时可以使用一个类似于函数的工具:“宏”,来完成判断,这个宏就被称为 assert 宏,翻译过来就是“断言”宏,用来判断它附加的前提表达式(是个布尔逻辑表达式)是否成立。

也就是写作 assert(前提-布尔表达式 is true); 。如果断言宏附加的布尔表达式不满足,则程序会以非0值退出。

下面是一段应用了 assert 宏的程序,其中为了求一个给定的正数的平方根,用了牛顿的“递归公式法”,这时要求“给定的数和递归的次数都为正数”;断言宏断言这个前提是满足的。


#include <iostream>
//  这是一个用 “牛顿法”公式 迭代求解某个正数的平方根的程序

#define NDEBUG
#include <cassert>  // 使用 assert 宏 需要引入的头文件


void getInput(double& num, int& i);
//  pre: 键盘输入 num - 需要求平方根的数  i - 公式计算迭代的次数
//  post:num 和 i 分别被设定为输入值

void introduction();
//  pre: 无
//  post: 介绍一下程序功能

double newtonSqroot(double n, int num_iterations);
//  pre: n是正数 num_iterations是正整数
//  post: 返回 n 的平方根 (通过牛顿公式的 num_iterations 次迭代来估算)

void giveOutput(double num, int i, double sq_root);
//  pre: num - 需要求平方根的数  i - 计算迭代的次数  sq_root - 求得的平方根
//  post:屏幕输出 num  i sq_root 的值

int main()
{
    double num, ans;
    int i; // ans 是 num 的平方根 (经过了i次迭代求得的近似解)
    introduction();
    getInput(num, i);
    
    ans = newtonSqroot(num, i);
    giveOutput(num, i, ans);
    return 0;
}

void getInput(double& num, int& i)
{
    using namespace std;
    cout << "请输入您要求平方根的数(需要大于0):";
    cin >> num;
    cout << "为了求这个平方根,您希望经过几轮计算?(算得越多越接近真实值)\n";
    cin >> i;
}

void introduction()
{
    using namespace std;
    cout << "依据牛顿法,每将整数N上一次求出的近似平方根x带入公式 (1/2)(x + N/x) \n" 
         << "求得的值会都比x更接近N的平方根。 本程序可以求一个整数的平方根的近似值: \n";
}
         
double newtonSqroot(double n, int num_iterations)
{
    double answer = 1;  // 既是n=1时的答案,也是第0次迭代时的初始值
    int i = 0;
    assert((n > 0) && (num_iterations > 0));    // 如果调用函数的前提不满足,这行语句将使程序因断言失败而退出
    while (i < num_iterations)
    {
        answer = 0.5 * (answer + n / answer);
        i++;
    }
    return answer;
}

void giveOutput(double num, int i, double sq_root)
{
    using namespace std;
    cout.setf(ios::fixed);
    cout.setf(ios::showpoint);
    cout.precision(4);
    cout << "经过 " << i << " 次计算,"; 
    cout << num << " 的平方根估计值为 " << sq_root << endl;
    cout << "感谢使用! \n";
}

递归的公式在代码中给出了,就是 \(sqrt_{i+1} = \frac{1}{2} \{ sqrt_i + \frac{n}{sqrt_i} \}\).

需要注意的除了 为了使用 assert,要引入库文件还有只要在代码中定义了“宏” NDEBUG (在代码开头有一行 #define NDEBUG),就可以让代码中的所有 assert 宏失效,也就是即使 assert宏 假设的前提不成立,程序也不会终止,而是就像调用assert宏的语句不存在一样继续运行。所以,要让文中的代码里的assert起到检查前提是否满足的调试功能,需要删掉开头 #define NDEBUG 这一句。

大概就是这样。

posted @ 2025-08-05 23:42  吴萝卜  阅读(28)  评论(0)    收藏  举报