一、 实验目的
- 利用多文件编程,掌握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");
    }
}
 

四、实验总结
好绕一指针!



















