1.熟悉Linux运行、开发环境。
2.掌握C语言编辑器的使用、gcc编译器的使用、gdb调试器的使用方法。
1.安装操作系统和开发环境
(1)安装Linux系统或虚拟机(建议Ubuntu 22.04 LTS)。
(2)写出你所装系统的发行版本和内核版本。
(3)安装编辑器(自选)、gcc编译器以及gdb调试工具。
2.编程
(1)编写一个C程序factorial.c。这个程序用于计算n的阶乘。程序可以接收输入的n,使用递归方法计算n的阶乘值fact并输出。使用gcc工具编译factorial.c程序使其可被gdb调试。调试中,设置断点观察fact的变化情况,使用gdb工具观察该程序递归调用过程,记录当fact>1000时的n值。
#include
int factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
int main() {
int n;
printf("请输入一个整数 n:");
scanf("%d", &n);
int fact = factorial(n);
printf("%d 的阶乘是 %d\n", n, fact);
return 0;
}
使用 gcc 工具编译这个程序并使其可以被 gdb 调试
gcc -g factorial.c -o factorial
当 fact 的值大于 1000 时,你可以使用 gdb 的观察功能记录 n 的值。以下是一种方式:
1. 进入 gdb 调试模式:在命令行中输入 `gdb factorial`。
2. 设置断点:输入 `break factorial`。
3. 启动程序:输入 `run`。
4. 当程序停在断点处时,使用以下命令观察变量 n 的值:
- 输入 `print n` 打印当前 n 的值。
- 输入 `condition $1 > 1000` 设定观察条件,当 n 的值大于 1000 时触发。
- 输入 `commands 1` 打开命令列表。
- 输入 `silent` 关闭输出。
- 输入 `continue` 继续执行程序。
- 输入 `end` 结束命令列表。
5. 继续执行程序:输入 `continue`。
6. 当 n 的值大于 1000 时,gdb 将停在断点处。你可以使用 `print n` 命令观察 n 的值。
7. 若要继续执行程序,请输入 `continue`。
8. 当程序终止时,输入 `quit` 退出 gdb。
在观察变量 n 时,你可以记录下触发条件的 n 值。这样你就可以知道当 fact 大于 1000 时的 n 值是多少。请注意,由于递归的特性,调试过程中断点可能会多次触发,每次触发时的 n 值可能不同。
(2)在Linux环境下,使用C编程实现下面的要求。输入至少3棵银杏树的地径、胸径、冠幅,保存为结构体数组,将输入的信息写入文件保存。
#include
#define MAX_TREES 3
typedef struct {
float trunkDiameter;
float chestDiameter;
float crownWidth;
} GinkgoTree;
void saveTreesInfoToFile(const GinkgoTree trees[]) {
FILE *file = fopen("trees_info.txt", "w");
if (file == NULL) {
printf("无法打开文件。\n");
return;
}
for (int i = 0; i < MAX_TREES; i++) {
fprintf(file, "地径: %.2f cm, 胸径: %.2f cm, 冠幅: %.2f cm\n",
trees[i].trunkDiameter, trees[i].chestDiameter, trees[i].crownWidth);
}
fclose(file);
printf("树木信息已成功保存到文件 trees_info.txt。\n");
}
int main() {
GinkgoTree trees[MAX_TREES];
printf("请输入至少 %d 棵银杏树的信息:\n", MAX_TREES);
for (int i = 0; i < MAX_TREES; i++) {
printf("第 %d 棵树:\n", i + 1);
printf("地径(单位:cm):");
scanf("%f", &trees[i].trunkDiameter);
printf("胸径(单位:cm):");
scanf("%f", &trees[i].chestDiameter);
printf("冠幅(单位:cm):");
scanf("%f", &trees[i].crownWidth);
}
saveTreesInfoToFile(trees);
return 0;
}
定义了一个 GinkgoTree
的结构体,用于存储银杏树的地径、胸径和冠幅。在 main
函数中,使用循环依次输入至少 3 棵银杏树的信息,并将其保存在 trees
结构体数组中。然后,调用 saveTreesInfoToFile
函数将树木信息写入名为 trees_info.txt
的文件中。
请确保在编译和运行代码之前,已在当前目录下创建了可写入的 trees_info.txt
文件。完成后,树木的信息将被写入该文件中。
(3)使用man分析strerror函数和perror函数,尝试使用strerror函数实现perror函数的功能。
`strerror` 函数和 `perror` 函数是用于处理错误信息的函数,下面是对这两个函数的简要说明:
1. `strerror` 函数:
- 函数定义:`char *strerror(int errnum);`
- 功能:将给定的错误码 `errnum` 转换为对应的错误信息字符串。
- 返回值:返回指向错误信息字符串的指针。
2. `perror` 函数:
- 函数定义:`void perror(const char *s);`
- 功能:根据全局的错误码 `errno` 打印对应的错误信息字符串,并在其前面加上字符串 `s`。
- 返回值:无。
要使用 `strerror` 实现 `perror` 的功能,可以编写一个自定义函数,模拟 `perror` 的行为。下面是一个实现示例:
#include
#include // 包含 strerror 函数的头文件
#include // 包含 errno 变量的头文件
void myPerror(const char *s) {
// 使用 strerror 函数将 errno 转换为错误信息字符串
const char *errorMsg = strerror(errno);
// 根据需要,可以将错误信息字符串与自定义的字符串连接起来
printf("%s: %s\n", s, errorMsg);
}
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (!file) {
myPerror("打开文件失败");
}
return 0;
}
在上述示例中,我们定义了一个自定义函数 `myPerror`,类似于 `perror`。它接收一个字符串 `s` 作为参数,并使用 `strerror` 函数将全局的错误码 `errno` 转换为错误信息字符串。
在 `main` 函数中,我们尝试打开一个不存在的文件,并在失败时调用 `myPerror` 函数输出错误信息。
这样,通过使用 `strerror` 函数,我们可以实现类似 `perror` 的错误处理功能。