Skip to content

头文件剖析

基本概念

​ 在一个C文件中除了main、跳转标号不需要声明之外所有的标识符在使用之前都需要进行声明,一般我们把函数的声明放在头文件中。

变量声明 != 变量定义 ===> 区别:是否分配内存

变量定义:生成相关的汇编指令

变量声明:告诉编译器,有可能存在于别的文件,编译时候先不要报错,在连接的时候到相应的文件中查找,没有 再报错

定义只有一次,声明可以有多次

C
  1 #include <stdio.h>                                                                    
  2 
  3 #ifndef __LCD_H__
  4 #define __LCD_H__
  5 
  6 #define PI 3.14
  7 void lcd_init(void);
  8 #endif
注:
  • 如果在头文件定义了新的数据类型,宏定义,就需要包含条件编译防止多次包含

隐式声明

==如果没有进行声明只产生一个警告,不会报错,并且增加一个隐式声明==

给出的隐式声明可能会与原函数的数据格式有区别,造成错误

变量声明与定义

extern:使用文件外的参数

C
//1.c
  1 int i = 0;                                                                            
  2 int a[10] = {1, 2, 3, 4, 5,6 ,7 ,8 ,9 , 0};
  3 struct student
  4 {
  5     int age;
  6     int num;
  7 };
  8 struct student stu = {20, 1001};
  9 int k;
 10 
//main.c
  1 #include <stdio.h>                                       
  2 
  3 extern int i;
  4 extern int a[10];
  5 struct student{
  6     int age;
  7     int num;
  8 };
  9 extern struct student stu;
 10 extern int k;
 11 
 12 int main(void)
 13 {
 14     printf("%s: i = %d\n", __func__, i);
 15     for(int j = 0; j<10 ; j++)
 16         printf("s[%d]: %d\n", j, a[j]);
 17     printf("stu.age = %d, num = %d\n", stu.age, stu.num);
 18     printf("%s: k = %d\n", __func__, k);
 19     return 0;
 20 
 21 }


result: 

main: i = 0
s[0]: 1
s[1]: 2
s[2]: 3
s[3]: 4
s[4]: 5
s[5]: 6
s[6]: 7
s[7]: 8
s[8]: 9
s[9]: 0
stu.age = 20, num = 1001
main: k = 0
  • 使用了extern无初始化语句则为声明
  • 没有使用extern无初始化语句则为试探性定义
  • 没有使用extern有初始化语句则为定义

试探性定义:该变量可能在别的文件中有声明,如果没有按照规则声明并初始化默认值:NULL, 0,

​ undefined value等 。如果别的文件中有定义,那把他当成声明

产生原因

​ 由于内存原因,早期的C语言一次只能编译一个文件,每个源文件单独编译,只编译一次

前向引用

没有声明就可以使用

  • 隐式声明(新的标准中被禁用)
  • 语句标号:跳转向后
  • 不完全类型:被定义完整之前用于特定的用途

不完全类型

  • void
  • 一个不确定大小的种类
  • 一个不确定的struct或者union
C
int array_print(int i[], int len);

struct LIST_NODE
{
    struct LIST_NONE *next;//在定义结束前已经使用
    int data;
};
goto error;

:在进行对不完全类型的引用时候只能使用类型属性,其他的如变量值,结构成员,大小等不能使用

​ 如果只是用指针就可以不用考虑这些

C
struct person;\\前向声明
struct student{
    struct person *p;
    int score;
    int no;
};

模块的封装

add.h

C
int add(int a, int b);

add.c

  • 包含自己的头文件:在编译时候进行检查,防止声明与定义不一致
C
#include "add.h"
int add(int a, int b)
{
    return 	a+b;
}

main.c

C
#include <stdio.h>
#include "add.h"

int main(void)
{
    int sum;
    sum = add(1, 2);
    return 0;
}

头文件路径

  • <>:标准头文件,官方路径
  • "":自定义的文件

可以使用gcc -I来指定目录

搜索顺序<>:

  • gcc -I
  • 环境变量指定
  • GCC内定文件

搜索顺序"":

  • 当前目录
  • gcc -I
  • 环境变量
  • GCC内定文件

Linux常用的路径

  • PATH:可执行文件搜索

  • C_INCLUDE_PATH: C语言头文件搜索路径

  • CPLUS_INCLUDE_PATH: c++头文件路径

  • LIBRARY_PATH:库文件搜索路径

头文件中的内联函数

  • 在多个模块调用这个文件的时候,内联函数已经被展开了,通常与static一同使用,确定作用域在本文件之中