C语言include

关注微信公众号塔容万物

include

include是C语言中的预处理指令,用来导入头文件(.h)或源文件(.c),include指令的语法如下:

#include <stdio.h>  // 导入系统头文件,使用<>包裹
#include "mylib.h"  // 导入自定义头文件,使用""包裹

除了可以导入头文件之外,还可以直接导入源文件,但是这种方式十分少见(不推荐)

// main.c
#include "mylib.c"

函数定义与声明

c语言规定,函数可以声明多次,但是只能定义一次,函数的声明和定义的区别如下:

// 函数声明
int add(int a, int b);

// 函数定义
int add(int a, int b) {
  return a + b;
}

为什么不推荐直接导入源文件

如果在多个源文件中导入了同一个源文件,那么这个源文件中的函数就会被重复定义,这样会导致编译错误。

比如在mylib中定义了一个add函数,在test.c中定义了一个sum函数,这个sum函数中有使用了add函数

// mylib.c
int add(int a, int b) {
  return a + b;
}

// test.c
#include "mylib.c"
int sum(int a, int b) {
  return add(a, b);
}

当在main.c中同时导入了mylib.c和test.c时,就会导致add函数被重复定义,从而导致编译错误

// main.c
#include "mylib.c"
#include "test.c"

int main() {
  return 0;
}

include会将被导入的文件的内容直接复制到导入文件中,所以在main.c中,add函数被定义了两次,从而导致编译错误。

// main.c
int add(int a, int b) {
  return a + b;
}

// 重复定义
int add(int a, int b) {
  return a + b;
}

int sum(int a, int b) {
  return add(a, b);
}

int main() {
  return 0;
}

将声明和定义分离

为了解决重复定义的问题,我们可以将函数的声明和定义分离,将函数的声明放在头文件中,将函数的定义放在源文件中,这样就可以避免重复定义的问题。

// mylib.h
int add(int a, int b);

// mylib.c
#include "mylib.h"
int add(int a, int b) {
    return a + b;
}

使用时,只需要导入头文件即可

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

int main() {
  int a = 1, b = 2;
  int c = add(a, b);
  printf("%d\n", c);
  return 0;
}

更高级的头文件

上面的方案貌似解决了重复定义的问题,但当函数的定义也发生在头文件中时,还是会导致重复定义的问题,比如下面的代码:

// mylib.h
int add(int a, int b) {
    return a + b;
}

// main.c
#include "mylib.h"
#include "mylib.h" // 重复导入

int main() {
  int a = 1, b = 2;
  int c = add(a, b);
  printf("%d\n", c);
  return 0;
}

编译时,会将头文件中的内容复制到源文件中,当头文件被包含多次时,就会导致函数的定义被重复定义,从而导致编译错误。

为了解决这个问题,我们可以使用#ifndef#define来避免头文件被重复导入,这样就可以避免函数的重复定义。

// mylib.h
#ifndef MYLIB_H // 会在include之前检查是否已经定义了MYLIB_H
#define MYLIB_H

int add(int a, int b) {
    return a + b;
}

#endif

// main.c
// 未定义MYLIB_H,所以会导入头文件
#include "mylib.h"
// 已经定义了MYLIB_H,所以不会导入头文件
#include "mylib.h" // 不会重复导入

int main() {
  int a = 1, b = 2;
  int c = add(a, b);
  printf("%d\n", c);
  return 0;
}

一个常见的文件结构

经过上面一系列的铺垫,我们可以将一个项目的文件结构设计如下:

.
├── main.c
├── mylib.c
└── mylib.h

其中,main.c是主文件,mylib.c是自定义的库文件,mylib.h是自定义的头文件,mylib.cmylib.h一起组成了自定义的库。

# 编译命令
gcc -o main main.c mylib.c