一、 实验目的
- 利用多文件编程,掌握Linux环境下C程序的编辑、编译、运行等操作。
- 掌握Makefile文件的编写、变量及隐式规则和模式规则的应用。
- 掌握Linux环境下main函数的参数。
- 掌握各类指针的应用。
二、 实验任务与要求
- 根据实验要求编写C语言程序;
- 写出各个程序的运行结果并分析; //错误调试 关键代码解释
- 根据要求写出Makefile文件;
- 利用Makefile文件对多文件程序进行编译。
三、 实验内容
- 开发一个计算器,要求多文件编程,文件之间的调用关系如下图:
(1) 在inputNum.c文件中输入一组数;
(2) 在Caculator.c文件中在对输入的一组数据进行运算,采用哪些运算请自行设计,但每种运算要单独放在一个自定义函数中,所有运算函数放在Caculator.c文件中;
(3) 在myDisplay.c文件中输出这一组数的各个运算结果。
提示:这里Caculator我就整了一个计算函数sumNums,但实际上可以写好多个,比如求一组数的平均数、求一组数中的最大值、最小值啥的…,这里只写了一个仅为演示
// Caculator.h
int sumNums(int arr[], int n);
// Caculator.c
#include "Caculator.h"
int sumNums(int arr[], int n) {
int sum = 0;
for (int i=0;i<n;i++){
sum += arr[i];
}
return sum;
}
// myDisplay.h
void display(int a[], int n);
// myDisplay.c
#include <stdio.h>
#include "Caculator.h"
#include "myDisplay.h"
void display(int a[], int n) {
printf("求和结果为:%d\n", sumNums(a, n));
}
// inputNum.h
void readNums(int arr[], int n);
// inputNum.c
#include <stdio.h>
#include "inputNum.h"
void readNums(int arr[], int n) {
printf("请输入 %d 个数,以空格隔开:\n", n);
for (int i=0;i<n;i++) {
scanf("%d", &arr[i]);
}
}
// main.c
#include "inputNum.h"
#include "myDisplay.h"
int main() {
int a[5];
readNums(a, 5);
display(a, 5);
}
- 将上述编写的多文件进行预处理、编译、汇编、链接几步来完成多文件的编译。注意每一步骤需要编译的文件扩展名。
gcc -c Caculator.c
gcc -c myDisplay.c
gcc -c inputNum.c
gcc -c main.c
gcc -o main main.o myDisplay.o inputNum.o Caculator.o
- 编写上述多文件程序的Makefile文件。
main: main.o myDisplay.o inputNum.o Caculator.o
gcc -o main main.o myDisplay.o inputNum.o Caculator.o
main.o: main.c
gcc -c main.c
inputNum.o: inputNum.c
gcc -c inputNum.c
myDisplay.o: myDisplay.c
gcc -c myDisplay.c
Caculator.o: Caculator.c
gcc -c Caculator.c
clean:
rm *.o
- 编写上述程序的Makefile文件,要求在文件中使用自定义变量和预定义变量。
OBJ = main.o myDisplay.o inputNum.o Caculator.o
TARGET = main
$(TARGET): $(OBJ)
gcc -o $@ $^
main.o: main.c
gcc -c $<
inputNum.o: inputNum.c
gcc -c $<
myDisplay.o: myDisplay.c
gcc -c $<
Caculator.o: Caculator.c
gcc -c $<
clean:
rm $(OBJ) $(TARGET)
- 根据隐式规则和模式规则进行Makefile文件的编写。
OBJ = main.o myDisplay.o inputNum.o Caculator.o
TARGET = main
CFLAGS = -Wall -g
$(TARGET): $(OBJ)
$(CC) $^ -o $@ $(CFLAGS)
%.o: %.c
$(CC) -c $< -o $@ $(CFLAGS)
clean:
rm $(OBJ) $(TARGET)
- 利用gdb调试工具对上述程序生成的可执行文件进行调试,在各个自定义函数中设置断点,单步执行并查看断点处变量和数组的值。
提示:进行调试前需要用 隐式规则和模式规则版本(上面标号为5的)的 makefile
来构建可执行程序。原因是这个版本的makefile 刚好有 -g
选项
构建好之后,运行 gdb main
进入调试
(gdb) list
(gdb) break 6
(gdb) break 7
(gdb) run
(gdb) print a
(gdb) next
(gdb) print a
- 请利用main函数的参数编写程序实现计算器的功能。
如:运行程序及结果如下:
tarena@ubuntu:~/test/d1$ ./t1 5 + 8
5 + 8=13.000000
tarena@ubuntu:~/test/d1$ ./t1 9 / 3
9 / 3=3.000000
// t1.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
int a = atoi(argv[1]);
char oper = argv[2][0];
int b = atoi(argv[3]);
double ans = 0;
if (oper == '+') {
ans = a + b;
} else if (oper == '-') {
ans = a - b;
} else if (oper == '*') {
ans = a * b;
} else if (oper == '/') {
ans = (double)a/b;
}
printf("%d%c%d=%.6lf\n",a,oper,b,ans);
}
- 深入理解数组指针和指针数组的含义,利用数组指针、指针数组以及指向指针的指针分别输出二维数组的值,程序结果如下图所示。
#include <stdio.h>
int main() {
int arr[4][4] = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
{12, 13, 14, 15}
};
printf("使用指针数组的方式访问二维数组arr\n");
int *(ps[4][4]);
for (int i=0;i<4;i++){
for (int j=0;j<4;j++){
ps[i][j] = &arr[i][j];
printf("%d\t", *ps[i][j]);
}
printf("\n");
}
printf("使用数组指针的方式访问二维数组arr\n");
for (int i=0;i<4;i++){
int (*pa)[4] = arr + i;
for (int j=0;j<4;j++){
printf("%d\t", *(*pa + j));
}
printf("\n");
}
printf("使用指向指针的指针的方式访问二维数组arr\n");
for (int i=0;i<4;i++){
int *pa = *(arr + i);
for (int j=0;j<4;j++){
int *pb = pa + j;
printf("%d\t", *pb);
}
printf("\n");
}
}
四、实验总结
好绕一指针!