头文件

我前面关于stack.c和main.c的讨论。 stack.c这个模块封装了top和stack两个变量,导出了push、 pop、 is_empty三个函数接口,已经设计得比较完善了。但是使用这个模块的每个程序文件都要写三个函数声明也是很麻烦的,假设又有一个foo.c也使用这个模块, main.c和foo.c中各自要写三个函数声明。重复的代码总是应该尽量避免的,答案就是可以自己写一个头文件stack.h:

/* stack.h */
#ifndef STACK_H
#define STACK_H
extern void push(char);
extern char pop(void);
extern int is_empty(void);
#endif

这样在main.c中只需包含这个头文件就可以了,而不需要写三个函数声明:

/* main.c */
#include <stdio.h>
#include "stack.h"
int main(void)
{
push('a');
push('b');
push('c');
while(!is_empty())
putchar(pop());
putchar('\n');
return 0;
}

对于用角括号包含的头文件,gcc首先查找-I选项指定的目录,然后查找系统的头文件目录;而对于用引号包含的头文件, gcc首先查找包含头文件的.c文件所在的目录,然后查找-I选项指定的目录,然后查找系统的头文件目录。

假如三个代码文件都放在当前目录下:

$ tree
.
|-- main.c
|-- stack.c
`-- stack.h
0 directories, 3 files

则可以用gcc -c main.c编译, gcc会自动在main.c所在的目录中找到stack.h

假如把stack.h移到一个子目录下:

$ tree
.
|-- main.c
`-- stack
|-- stack.c
`-- stack.h
1 directory, 3 files

则需要用gcc -c main.c -Istack编译。用-I选项告诉gcc头文件要到子目录stack里找。

在#include预处理指示中可以使用相对路径,例如把上面的代码改成#include "stack/stack.h",那么编译时就不需要加-Istack选项了,因为gcc会自动在main.c所在的目录中查找,而头文件相对于main.c所在目录的相对路径正是stack/stack.h。

重复包含头文件有以下问题:

1. 一是使预处理的速度变慢了,要处理很多本来不需要处理的头文件。

2. 二是如果有foo.h包含bar.h, bar.h又包含foo.h的情况,预处理器就陷入死循环了(其实编译器都会规定一个包含层数的上限)。

3. 三是头文件里有些代码不允许重复出现,虽然变量和函数允许多次声明(只要不是多次定义就行),但头文件里有些代码是不允许多次出现的,比如typedef类型定义和结构体Tag定义等,在一个程序文件中只允许出现一次。

posted @ 2018-04-08 08:53  刘-皇叔  阅读(179)  评论(0编辑  收藏  举报