Lab6 使用工具
IMPORTANT
开始 Lab5 前请确保提交了 Lab4,请使用 git commit --all
进行提交
使用 git pull
更新任务仓库
你可能需要使用 git pull --rebase
来处理分支冲突
GCC、多文件编译和 Makefile
Task1 错误与警告
尝试编译 task1.c
,将所有的警告视为错误,你看到了几个错误?它们是什么错误?尝试修改 task1.c
的代码,让它可以正常运行,输出为 task1.out
。
Task2 多文件编译和奇怪的编译错误
在 zuma
文件夹下,包含了 include
文件夹和 src
文件夹, include
中包含了头文件, src
中包含了源代码,是的,这就是 lab5 中的代码,但是我们对代码结构进行了一些修改,增加了头文件和源文件。不修改任何文件,使用 gcc
编译运行。在使用 gcc
的命令行正确地设置了 include 路径之后,你应该还会看见一些错误,阅读 gcc
输出的错误提示,想想这是为什么?尝试不借助 AI,修改头文件,想想,你的方式可以应用在实际的工程之中吗?
Task3 基础 makefile 使用
尝试自己编写 makefile 文件(不要查看 lab5 的 makefile),使在命令行输入 make run
后,直接可以运行程序。你的脚本应该在任何源文件(包括头文件)发生更改的时候,重新编译程序。如果增加了源文件,你的脚本还可以正常工作吗?如果不可以,尝试编写一个能够在添加或删除 src
或 include
目录下 .c
或 .h
文件仍旧可以正常工作的脚本,你可能需要 shell 命令或者 makefile 内置函数。如果编译器需要从 gcc
改为 clang
,你的脚本可以很方便地进行修改吗?
Task4 清理垃圾
在 makefile 脚本中增加一个 clean
命令用于清理。
Task5 加速编译
在一般的工程中,往往有许多源文件,如果在一个文件修改时,需要重新编译所有文件,就会浪费很多时间。你有什么解决办法?带着你的办法继续看 Task5。
Task6 加速编译
一个办法是使用中间文件 .o
,它们是编译好的二进制文件,只需要正确地链接就可以作为最终目标文件的一部分。将一个个 .c
文件编译为 .o
文件,再进行链接,这样修改一个 .c
文件,就只需要重新编译那一个文件,再进行链接,这可以节省大量的时间。修改你的 makefile 脚本,实现这个功能,例如 main.c
应该编译生成 main.o
,最后使用 gcc
进行链接。什么?你不知道什么是链接和怎么用 gcc
链接?Ask the friendly AI! 我们暂时不要求在头文件发生更改时 makefile 脚本不会重新编译的问题。
Task7 (可选) 处理头文件
怎么解决 Task6 遗留的问题? gcc
生成的一类中间文件 —— 预处理完成的文件或许是一个关键点。当然还有其他方法,尝试阅读 gcc
的 -MMD
的用法找到答案。
Task8 配置 Vscode 的 clangd
VScode 的 clangd 插件通过运行在你的电脑上的 clangd 服务器来提供代码补全、错误提示等功能。而 clangd 服务器又依赖于编译命令,获得了正确的编译命令,clangd 就可以找到正确的头文件、定义宏等。或许以前自己编写 clangd 的配置文件是一件很容易的事情,因为你只有少数几个文件,但加入你要管理一个大项目,一个个文件手动输入恐怕不是个好方法。 bear
是一个方便的工具,可以从 makefile 中提取编译命令,直接输出 clangd 需要的配置文件,尝试搜索 bear
的用法,为你的 zuma 小项目一键生成 clangd 配置文件。
调试工具
Task9 断言
assert(expr)
是一个宏,当运行到 assert
并且 expr
的值为 0
( false
)的时候,程序会异常退出。退出时,你会看到退出的行号、被断言为假的表达式等等,方便我们 debug。 assert
有两个作用:
- 用作在测试中,判断测试结果是否正确,如果不正确,可以直接退出程序。
- 防御性编程。当某些非法操作可能会引发严重问题,或者需要排除程序中不易发现的 bug 的时候,判断到非法输入或者结果直接抛出异常。
Task10 初现端倪
既然你已经学会了多文件编译,粗略地阅读并且尝试编译运行我们的 pwd_checker
文件夹下的项目,后面我们还需要进行多次编译,如果你觉得每次需要输出一串指令很麻烦,别忘了使用你学过的工具。运行,你会发现一个错误,一个 assert
断言失败了。
Task11 使用 GDB
使用 GDB,找到错误的原因,修复 bug,完成之后,你的代码应该可以通过所有测试(不要修改 test.c
中的内容!)。你应该会使用到一些 GDB 的基本功能,例如步进、断点等等,如果你忘记了用法,请阅读手册。你当然可以询问 AI,但是你必须要有阅读手册的能力,相信我,你总有一天会遇到在手册上写得清清楚楚但是 AI 却回答不出来的问题。你也可以使用 printf
调试法,但是我们不推荐。
Task12 段错误
编译运行 task12.c
,你会发现一个段错误 Segmentaion fault
,这意味着我们进行了一些非法操作。使用 gdb
,定位问题,进行修改。
Task13 Bork 语言
粗略地阅读 task13.c
的代码,它将现代英语翻译为一种叫做 bork 的语言,尝试编译运行,翻译几个常用的单词,但是似乎出现了一些奇怪的字符,这些字符似乎不应该属于 bork 语言,看来我们的程序出现了问题。
$ gcc ./task13 -o task13
$ ./task13 cstart
Input string: "cstart"
Length of translated string: 30
Translate to Bork: "cs�S�t
�� \af�� \r
�� \t
�� \"
2
3
4
5
6
7
8
使用 valgrind
,尝试定位问题并修复。