C语言中文件包含.c文件、.h文件与直接多文件的区别与应用总结

  我们通常在完成一个程序时,往往习惯将程序写为多文件的,这样就能实现程序的模块化管理,以及分工开发合作。而一些全局变量,全局函数,结构体等就能使各模块联系在一起。

  在平常大家写代码的过程中,通常会使用文件包含来联系各代码文件,当然初学者也可能会直接写成多文件程序,没有文件包含,这样也能编译、运行。

  在这里,写了一些小段的测试代码,来说明:包含.c文件,,直接多文件,包含.h文件三种方式的区别与应用。

1.包含.c文件

  可能有人不习惯写.h文件,而直接将代码写在.c文件中,其他文件要调用函数则直接包含.c文件。正如下面一段代码:

//fun.c
int h = 0;
int cal(int a, int b)
{
    return (2*a+3*b);
}


//main.c
#include "stdio.h"
#include "fun.c"   //包含.c文件
int main(void)
{
    h = 1;   //直接使用fun.c中的变量
    int i = 1,j = 2;
    int c = 0;
    c = cal(i, j);  //直接调用fun.c中的函数
    printf("%d\n", c);
    return 0;
}

        编译:gcc main.c -o main     直接编译通过,运行正确。

我们再看一个测试程序:

//fun.c
int h = 0;
int cal(int a, int b)
{
    return (2*a+3*b);
}

//fun1.c
#include "fun.c"   // 就这一句

//main.c
#include "stdio.h"
#include "fun.c"   //包含.c文件
#include "fun1.c"   //添加包含fun1.c
int main(void)
{
    h = 1;   //直接使用fun.c中的变量
    int i = 1,j = 2;
    int c = 0;
    c = cal(i, j);  //直接调用fun.c中的函数
    printf("%d\n", c);
    return 0;
}

       编译:gcc main.c -o main   编译报错:h  cal  发生了重定义。

总结:

        优点可以直接编译main.c文件,不用编译fun.c,因为在main.c的编译预处理阶段,会将fun.c 的代码直接拷贝过来。读者可以使用gcc -E main.c -o main.i  查看预处理后.i文件的代码。发现main.i中将fun.c的代码拷贝了过来。

   缺点:fun.c不能被多个文件包含,因为这样就会产生变量和函数的多个拷贝,造成严重的重定义,编译通不过。

        这种方式可解决较简单程序的文件包含问题,通常不推荐使用。

2.直接多文件

       多文件程序中,全局变量可以被外部引用,函数也默认是全局函数,可以被外部使用。废话少说,先上代码:

//fun.c
int global_var = 0;

int cal(int a, int b)
{
    return (2*a+3*b);
}

//main.c
#include "stdio.h"

extern int  global_var;  // 声明外部变量

int main(void)
{
    global_var =1;  //外部变量赋值
    int i = 1,j = 2;
    int c = 0;
    c = cal(i, j);  //外部函数调用
    printf("%d\n", c);
    return 0;
}

Makefile书写为:

CFLAG=-Wall -O -g

main:main.o fun.o
gcc $(CFLAG) main.o fun.o -o main
main.o:main.c
gcc $(CFLAG) -c main.c -o main.o
fun.o:fun.c
gcc $(CFLAG) -c fun.c -o fun.o
clean:
rm main.o fun.o main

编译通过,会出现警告信息,说cal为隐含的外部函数。运行正确。结果还算理想,那么我们再来看一段:

 1 int global_var = 0;
 2 struct student
 3 {
 4     int age;
 5     int num;
 6 };                     //定义结构体
 7 struct student wang;     //定义结构体变量
 8 int cal(int a, int b)
 9 {
10     return (2*a+3*b);
11 }
12 
13 #include "stdio.h"
14 extern int  global_var;
15 extern struct student wang;   //声明外部结构体变量
16 int main(void)
17 {
18     wang.age = 10;  //报错
19     student li;    //想使用外部的定义的结构体来定义一个变量   报错
20     global_var =1;
21     int i = 1,j = 2;
22     int c = 0;
23     c = cal(i, j);
24     printf("%d\n", c);
25     return 0;
26 }

同样的编译,main.c文件的编译会出现错误,首先是想使用外部定义的结构体变量(已作声明),报错。然后,想用外部结构体定义一个变量,报错。

总结:

  优点不会出现重定义问题

  缺点1、如果要用到的外部变量多,或是在多处要使用外部变量,则要在每一个调用的文件中使用extern声明,比较麻烦。

       2、如果是在fun.c中有结构体定义或是类的定义,在main.c中用使用结构体定义变量,或是使用类定义对象。编译不能通过,不知该如何处理。

       3、如果是定义在fun.c中的结构体变量,在main.c中要声明外部变量,并使用,会出错。故推知:结构体不能声明为外部变量,只能是包含的头文件中定义的结构体。

解决上述问题的方式就是第三种,包含.h文件方式。

3.包含.h文件

  为每一个模块都编写一个.c文件,一个.h文件。

  .c源文件中放:函数的定义,全局变量的定义,全局结构体变量的定义。

  .h头文件中放:函数的声明,全局变量的声明(extern),全局结构体的定义,全局结构体变量的声明。

  调用文件(main.c)文件中:包含.h头文件即可。不用声明任何东西。

  测试代码如下:

//fun.c
#include "fun.h"

int global_var = 0;

struct student wang;  //结构体变量定义

int cal(int a, int b)
{
    return (2*a+3*b);
}

//fun.h
#ifndef _fun_h_  //条件编译,解决文件重复包含所引起的重定义问题
#define _fun_h_

struct student
{
    int age;
    int num;
};

extern int global_var;
extern struct student wang;

#endif

//fun1.c
#include "fun1.h"  //没有实际代码

//fun1.h
#include "fun.h"  //包含fun.h

//main.c
#include "stdio.h"
#include "fun.h"
#include "fun1.h"

//extern int  global_var;
//extern struct student wang;
int main(void)
{
    wang.age = 10;   //调用全局结构体变量
    struct student li;  //定义变量
    global_var =1;  //使用全局变量

    int i = 1,j = 2;
    int c = 0;
    c = cal(i, j);
    printf("%d\n", c);
    return 0;
}

 总结:

    优点:解决了文件重复包含造成的重定义问题;函数,全局变量,结构体,结构体变量都能在外部调用;每一处调用变量的文件处不用重复的写声明语句,因为声明语句直接写在了.h头文件中,直接包含头文件即可。

转载于:https://www.cnblogs.com/goaheadnohesitate/p/3287807.html

猜你喜欢

转载自blog.csdn.net/weixin_33795093/article/details/94705073
今日推荐