一、实验目的
1、练习并掌握 Linux 提供的 vi 编辑器来编译 C 程序
2、学会利用 gcc、gdb 编译、调试 C 程序
3、学会使用 make 工具
二、实验环境
阿里云服务器,操作系统为CentOS 7.9 64位
三、实验过程
1. 编写一个简单的 C 语言程序:输出两行文字“How are you”,在 Linux 下编辑、编译、运行。
2. 编写一个简单的 C 语言程序:根据输入的两个整数求平均值并且在终端输出,通过 gcc 编译器得到它的汇编程序文件。
3. Makfile 文件中的每一行是描述文件间依赖关系的 make 规则。
对于下面的 Makefile 文件:
CC = gcc OPTIONS = -O3 -o
OBJECTS = main.o stack.o misc.o
SOURCES = main.c stack.c misc.c
HEADERS = main.h stack.h misc.h
polish: main.c $(OPJECTS)
$(CC) $(OPTIONS) polish $(OBJECTS) -lm
main.o: main.c main.h misc.h
stack.o: stack.c stack.h
misc.h misc.o: misc.c misc.h
回答:
a. 所有变量名字
CC,OPTIONS
b. 所有目标文件的名字
main.o,stack.o,misc.o
c. 每个目标的依赖文件
main.o:main.c,main.h,misc.h
stack.o:stack.c,stack.h,misc.h
d. 生成每个目标文件所需执行的命令
cc -c main.c
cc -c stack.c
cc -c misc.c
e. 画出 makefile 对应的依赖关系树。
f. 生成 main.o,为什么?
因为需要将编译后的代码变成汇编代码,生成main.o文件和Target有依赖关系,想要编译成功target必须生成main.o文件。
4.用编辑器创建 main.c, compute.c, input.c, compute.h, input.h 和 main.h 文件。下面是 它们的内容。注意 compute.h 和 input.h 文件仅包含了 compute 和 input 函数的声明 但没有定义。定义部分是在 compute.c 和 input.c 文件中。main.c 包含的是两条显示 给用户的提示信息。(相关程序代码如下)
[user@localhost user]$ cat compute.h
/*compute 函数的声明原型*/
double compute(double,double);
[user@localhost user]$ cat input.h
/*input 函数的声明原型*/
double input(char*);
[user@localhost user]$ cat main.h
/*声明用户提示*/
#define PROMPT1 "请输入 x 的值:"
#define PROMPT2 "请输入 y 的值:"
[user@localhost user]$ cat compute.c
#include<stdio.h>
#include<math.h>
#include "compute.h"
double compute(double x,double y) {
return (pow((double)x,(double)y));
}
[user@localhost user]$ cat input.c
#include<stdio.h>
#include "input.h"
double input(char *s) {
float x;
printf("%s",s);
scanf("%f",&x);
return (x);
}
[user@localhost user]$ cat main.c
#include<stdio.h>
#include "main.h"
#include "compute.h"
#include "input.h"
int main(int argc, char *argv[]) {
double x,y;
printf("本程序从标准输入获取 x 和 y 的值并显示 x 的 y 次方.\n");
x=input(PROMPT1);
y=input(PROMPT2);
printf("x 的 y 次方是:%6.3f\n",compute(x,y));
return 0;
}
结合上面代码创建 Makefile 文件,使用 make 命令,生成 power 可执行文件,并运行 power 程序。给出完成上述工作的步骤和程序运行结果。
5. 下面程序的功能是提示你输入一个整数并把它显示到屏幕上,现在它能够通过编译 但运行不正常。利用 gdb 找出它的错误并改正它。重新编译和运行改过的程序以确 保它工作正常。
#include<stdio.h>
#define PROMPT "请输入一个整数:"
void get_input(char *, int *);
void main() {
int *user_input;
get_input(PROMPT, user_input);
(void) printf("你输入了:%d。\n", user_input);
}
void get_input(char *prompt, int *ival){
(void) printf("%s", prompt);
scanf("%d", ival);
}
编译成功但是运行出错,系统提示段错误,估计是非法访问内存之类的问题
开始用gdb调试程序
可以看到user_input开始没初始化,把默认的NULL传给了形参 ival ,SEGV应该是Segmentation Violation的意思。
NULL 的地址是 0x0 ,在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。
调试结束后修改程序代码,为user_input分配空间
这里发现虽然运行没报错,但输出结果是有问题的,很容易想到是print时没给user_input解引用从而把地址给输出来了,如果要继续调试的话的步骤都差不太多,这里直接修改程序代码,运行成功。
四、实验心得
在Linux上编写 C 语言程序,学会了用gcc 编译并观察编译后的结果,运行生成的可执行文件。学会利用 gdb 调试程序,学会了如何编写 makefile,并进行编译。