《C语言编程初学者指南》一1.8 调试C程序

简介: 本节书摘来自华章出版社《C语言编程初学者指南》一书中的第1章,第1.8节,作者【美】Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云栖社区“异步社区”公众号查看 1.8 调试C程序 如果你的程序编译了,然后退出了或在执行中出现异常,程序中一定存在一个错误(一个bug)。

本节书摘来自异步社区《C语言编程初学者指南》一书中的第1章,第1.8节,作者【美】Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云栖社区“异步社区”公众号查看

1.8 调试C程序

如果你的程序编译了,然后退出了或在执行中出现异常,程序中一定存在一个错误(一个bug)。我们将要花费很多的时间来找到并删除这些bug。本小节介绍了帮助你开始这一工作的一些技巧。然而,请记住,调试是计算机科学,同样也是一门艺术,当然,你的编程实践越多,调试也就变得越容易!往往一个程序编译和执行得很好,但总是产生你意料之外的或者不想要的结果。例如,如下的程序的编译和执行没有错误,但是输出却是无法读懂的,或者说不是我所期望的,其输出如图1.11所示。

include <stdio.h>

int main()
{
    printf("Chapter 1 - Getting Started with C Programming");
    printf("This is an example of a format bug."); 
    printf("The format issue can be corrected by using"); 
    printf(" the \n and \\ escape sequences");
    return 0;
}


9cbfd9e1a1d5807f727c401de7bffe11a10bc021

图1.11 错误的格式化

你能看到格式有什么问题吗?漏掉了什么,应该在哪里进行修改?下一段代码及其输出如图1.12所示,它通过放置相应的转义序列而改正了格式。


52cea7862d38ee8ca7b86c5a321e7b3c4cb06a8a

图1.12 通过在需要的地方添加n和\转义序列,改正了格式

include <stdio.h>

int main()
{
    printf("Chapter 1 - Getting Started with C Programming\n");
    printf("This is an example of a format bug.\n"); 
    printf("The format issue can be corrected by using"); 
    printf(" the \\n and \\\\ escape sequences");
    return 0;
}

格式问题是编程初学者经常遇到的问题。通常,可以通过熟练使用printf()函数和各种转义序列来快速地解决这些问题。

另一类常见的bug是逻辑错误,包括循环在期望退出的时候没有退出、数学计算公式的错误或者可能是一次有瑕疵的相等性测试(条件)。调试逻辑错误的第一个步骤是,找到程序bug存在的第一行代码。做到这一点的一种方式是使用打印语句,也就是在整个代码中分散地使用printf()函数。例如,可以在源代码中做一些如下的事情:

anyFunction(int x, int y)
{
    printf("Entering anyFunction()\n"); fflush(stdout);
    —— lots of your code here ———
    printf("Exiting anyFunction()\n"); fflush(stdout);
}

fflush()函数确保了printf语句的结果会立即发送到屏幕,并且如果你想要使用printf()进行调试的话,应该使用fflush()函数。传递给fflush()函数的stdout参数是标准输出,这通常是计算机屏幕。

在将发生逻辑错误的地方的范围缩窄到代码行或函数之后,下一步就是搞清楚你的变量在彼时的值。还是可以使用printf()函数来打印出变量值,这对于确定非正常的程序行为的源头有很大的帮助。第2章将详细介绍使用printf()函数来显示变量值。

记住,当你修复了任何的bug之后,必须重新编译程序,运行它,并且如果必要的话,再次进行调试。

作为程序员新手,你经常会遇到的是编译错误,而不是逻辑错误,而编译错误通常是语法问题所导致的,例如漏掉了标识符和终结符,或者使用了无效的指令、转义序列或注释语句块等语法问题。

调试编译错误可能会令人沮丧,特别是当你在计算机屏幕上看到50条或者更多的错误的时候。需要记住的重要的一点是,程序开始处的一个错误,可能在编译的时候导致一系列层叠性的错误。因此,开始调试编译错误的最好的地方,就是错误列表中的第一个错误。在接下来的几个小节中,我们将介绍在刚开始编写C程序的时候的一些较为常见的编译错误。

1.8.1 常见错误之1:漏掉程序语句块标识符
如果忘记了插入一个开始或对应的结束程序语句块的标识符({和}),你将会看到如图1.13所示的错误消息。在下面的示例中,我们故意在main()函数名的后面,漏掉开始程序语句块标识符({}。

include <stdio.h>

int main()
    printf("Welcome to C Programming\n");
return 0;
}


c8d2c65796baac1768ab49cdb9b7bd125ef12104

图1.13 由于漏掉了程序语句块标识符而导致的错误

由于忘记使用开始程序语句块标识符({),导致了很多的错误,如图1.13所示。当调试编译错误的时候,记住要从第1个错误开始,如下所示,它告诉你就在printf()函数之前有一个错误。你将会发现,解决了第1个错误之后,也就更正了很多甚至是所有剩下的错误。

hello.c:5:2: error: expected declaration specifiers before 'printf' 
  printf("Welcome to C Programming\n");
  ^

技巧
 

为了帮助你找到一个错误的位置,编译器试图向你显示捕获到错误的代码的行号。在前面的示例中,hello.c:5:是告诉你在hello.c源代码的第 5 行捕获到了错误。注意,编译器在统计行号的时候,将空行和代码行都算在内。
尽管编译器错误指向了printf开始处,但重要的是要知道,可能并不是printf而是在它之前的某处出现了错误,在这个例子中,是因为漏掉了语句块标识符。

1.8.2 常见错误之2:漏掉语句终结符
图1.13展示了由几个常见场景所导致的一条常见错误消息。这种类型的错误可能会由于几个原因而导致。除了漏掉程序语句块的缩进,漏掉了语句终结符(分号)也可能会导致解析错误。

图1.14展示了如下程序中的一个bug。你能看出这个bug隐藏在哪里吗?

include <stdio.h>

int main()
{
    printf("Welcome to C Programming\n")
    return 0;
}


80e0756c0fa34e41486175384ba8b9c62459e0bd

图1.14 漏掉终结符导致的错误

由于C编译器不能够确定一条程序语句(如一条打印语句)到哪里结束,导致了解析错误。在图1.14所示的例子中,C编译器(gcc)告诉你,它期望在第6行的return语句之前有一个分号(语句终结符)。

1.8.3 常见错误之3:无效的预处理器指令
如果输入了一条无效的预处理器指令,例如,把库的名称拼写错了,你将会接收到一条如图1.15所示的错误消息。


73c45bf4a01b639ecdb38a569a6b45e81e96ad04

图1.15 无效的预处理器指令导致错误

在如下的程序语句块中,预处理器指令中的库名称拼写错了,导致了如图1.15所示的错误消息。你能看出错误吗?

include <sdio.h>

int main()
{
    printf("Welcome to C Programming\n");
    return 0;
}

由于不存在库文件sdio.h,导致了这个错误。标准输入输出库的名称应该是stdio.h。

1.8.4 常见错误之4:无效的转义序列
在使用转义序列的时候,常常会使用无效的字符或者无效的字符序列。例如,图1.16展示了一个无效的转义序列所导致的错误。gcc 编译器更为具体地指出了这个错误,如图 1.16所示。特别是,它指出错误在第7行,并且指明这是一个未知的转义序列。


ad3b1b747d8027dbc9755125b092bc860085ba1c

图1.16 无效的转义序列导致的错误

你能够找出如下的程序中的无效的转义序列吗?

include <stdio.h>

int main()
{
    printf("Welcome to C Programming\m");
return 0;
}

用诸如n这样的一个有效的序列,来替代无效的转义序列m,就可以改正这个问题。

1.8.5 常见错误之5:无效的注释语句块
正如本章前面的1.3节所提到的,无效的注释语句块也会导致编译错误,如图1.17所示。

include <stdio.h>

int main()
{
    */ This demonstrates a common error with comment blocks /*
    printf("Welcome to C Programming\n");
    return 0;
}


5842d511a7c2090f7bbac259a373498f15f2b723

图1.17 无效的注释语句块导致错误

对于注释语句块进行一个简单的修改,如下所示,就可以解决这个问题,并能够让程序成功地编译。

/* This corrects the previous comment block error
相关文章
|
29天前
|
C语言
最简单的C语言程序示例
最简单的C语言程序示例
19 0
|
29天前
|
Serverless C语言
C语言程序通常具有以下基本结构
C语言程序通常具有以下基本结构
10 0
|
29天前
|
监控 网络协议 API
C语言系统编程
C语言系统编程
|
1月前
|
C语言 C++
第一个c语言程序
第一个c语言程序
|
18天前
|
C语言
C语言使用宏定义实现等级调试输出PRINT_LEVEL
C语言使用宏定义实现等级调试输出PRINT_LEVEL
|
11天前
|
存储 编译器 C语言
深入探索C语言动态内存分配:释放你的程序潜力
深入探索C语言动态内存分配:释放你的程序潜力
26 0
|
1月前
|
C语言
C语言最基本程序控制结构
C语言最基本程序控制结构
13 0
|
18天前
|
Linux 测试技术 C语言
【Linux】应用编程之C语言文件操作
【Linux】应用编程之C语言文件操作
|
24天前
|
存储 程序员 编译器
C语言第十三弹---VS使用调试技巧
C语言第十三弹---VS使用调试技巧
|
28天前
|
存储 Serverless C语言
C语言程序的结构
C语言是一种广泛使用的编程语言,其程序结构清晰,易于理解。下面我们将详细探讨C语言程序的基本结构,并通过一个示例代码来展示这些组成部分是如何协同工作的。 一、C语言程序的基本结构
14 0