模块划分
在实际应用中,一个较大的 C 程序并不会把所有代码都放入 main 主函数中,而是进行模块化设计,每个程序模块作为一个源程序文件,再由若干源程序文件组成一个 C 程序。这样处理便于分别编写、分别编译、进而提高调试效率。
#include <stdio.h>
void func1(){ //函数声明并定义
printf("hello\n");
}
int func2(int i); //函数声明
int main() {
func1(); //函数调用
int i = func2(3); //函数调用
printf("%d\n",i);
return 0;
}
int func2(int i) { //函数定义
printf("%d\n",i);
return i+2;
}
现在将上述代码拆分为两个源程序文件 func.c 和 main.c,同时添加一个头文件 func.h。
一般情况下在 .h 文件中进行变量、函数和宏的声明,在 .c 文件中进行变量和函数的具体实现。
func.h 文件如下:
#include <stdio.h>
int func2(int i);
void func1();
func.c 文件如下:
#include "func.h"
void func1(){
printf("hello\n");
}
int func2(int i) {
printf("%d\n",i);
return i+2;
}
main.c 文件如下:
#include "func.h"
int main() {
func1(); //函数调用
int i = func2(3); //函数调用
printf("%d\n",i);
return 0;
}
Tips:
#include <> 是从 C 语言标准库中查找头文件;
#include "" 是从源代码所在目录中查找头文件,一般自己编写的头文件用引号。
ps:为什么经常见 xx.c 中 include 对应的 xx.h?因为如果 .c 中的函数也需要调用同个 .c 中的其他函数,那么这个 .c 往往会 include 同名的 .h,这样就不需要为声明和调用顺序而发愁。这已经成为了一种代码规范。
编译器工作原理
- 预处理阶段:实际上是处理 define 和 include 等宏命令,进行宏替换。例如
#include "xx.h"
就是把这一行删掉,把 xx.h 中的内容原封不动地插入在当前行位置。 - 编译阶段:以 .c 文件为基本单位进行。这一阶段为所有 .c 文件中的变量、函数分配空间,并将各个全局变量、函数进行符号描述,编译、汇编成二进制码从而生成 .o 目标文件。(这一阶段并不关心变量的具体定义和函数的具体实现,只要存在变量和函数的相关声明即可通过编译)
- 链接阶段:以 .o 文件为基本单位进行,主要的工作是重定位各个目标文件的函数、变量,生成可执行文件。这个过程主要是找到变量和函数的具体定义和实现。
注意点
注意点一:C是否支持函数重载?
函数重载是函数的一种特殊情况:
C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
C语言是不支持函数重载的。因为编译的时候,两个重载函数,函数名相同,一个名字对应多个地址,在 func.o 中的符号表中表示歧义,链接时也存在冲突,所以不支持。
如果有需求,可以用static关键字修饰其中一个函数,把该函数限制在其所在文件里。
拓展:C++引入了函数名修饰规则,用修饰过的函数名去符号表中匹配或者查找,保证不冲突。
注意点二:舍弃 .h 文件?
如果舍弃 .h 文件,在 .c 文件中包含变量、函数的声明以及实现,这时候其他 .c 文件用 #include
去包含这个 .c 文件就会出现问题。
func.c 文件:
#include <stdio.h>
void func1();
void func1(){
printf("hello\n");
}
main.c 文件:
#include "func.c"
int main() {
func1();
return 0;
}
链接过程会出现 func1 函数重复定义的错误,因为编译生成的 main.o 和 func.o 中都有 func1 函数的具体实现。
可以对 main.c 做如下修改:
void func1();
int main() {
func1();
return 0;
}
如此编译后生成的 main.o 文件中就不再包含 func1 函数的实现。但是如果 func.c 中的函数很多,以及调用这些函数的 .c 文件也很多,这样在每个 .c 文件中手动挨个添加这些函数的声明就十分繁琐。.h 文件就是为了声明函数和变量方便而诞生的,将所有的变量和函数声明都写在 .h 文件中,其他 .c 文件只需 #include 相应的 .h 文件即可。
注意点三:.h 文件中 #ifndef、#define和#endif
.h 文件中 #ifndef、#define和#endif的作用是防止头文件在一个 .c 文件中被重复包含。
a.h 文件:
struct Node{
int val1;
int val2;
};
b.h 文件:
#include "a.h"
c.h 文件:
#include "a.h"
main.c 文件:
#include "b.h"
#include "c.h"
int main() {
return 0;
}
a.h 中的内容被两次复制到 main.c 中,产生 struct Node 重复定义错误。
修改 a.h 内容:
#ifndef _A_H
#define _A_H
struct Node{
int val1;
int val2;
};
#endif
那么 a.h 在 main.c 中只会被替换一次,避免了头文件的重复包含。
标签:文件,func1,main,函数,int,C语言,编译器,模块,include From: https://www.cnblogs.com/hzyuan/p/17962331