C语言笔记15

196 阅读3分钟

多文件代码

预处理指令#include

预处理指编译器编译源文件时 不认识函数printf 所以需要先让编译器认识这个函数 就需要预处理指令#include<stdio.h>
#include会将文件<stdio.h>中的代码复制到该预处理指令出现处 并删除该预处理指令 修改后的代码将另存为中间文件或直接输入编译器 然后使用经过预处理后的代码进行编译 编译器先读到了printf的定义 就认识它啦

#include的两种形式

  1. #include<文件名>尖括号 在编译器的包含目录中搜索文件
  2. #include"文件名"双引号 先在当前目录中搜索文件再到编译器包含目录中搜索文件

多文件代码

从代码到可执行文件要经历几个过程

  1. 预处理 执行预处理文件 修改源代码
  2. 编译 将预处理后的源文件代码转换为二进制目标文件
  3. 链接 将需要用到的目标文件合并为可执行文件

那么自己写一个print模拟一下printf吧 534a0b2a13eb0937d1cf06be83ae534.jpg

注意
main.c中不能写#include"print.c" 因为编译器是单个独立编译的 编译完成后目标文件main.obj中含有print函数 print.obj中也有一份print函数 链接时会出现同名函数的现象导致链接失败 我们只需要让编译器识别main.c中的标识符print就好了 因此只需要函数声明 不用定义
更不能#include<print.c> 仅会在编译器包含的目录中搜索文件 而编译器的目录文件中没有文件print.c

头文件

把多个类似print的函数声明写在一个文件里 那谁需要使用这种函数 就只需要包含这个文件
这种文件称为头文件 后缀是.h 不需要经过编译 仅供被其他文件包含
下面这个print的用法就非常类似printf了 af9404cc4a572dae6ebe1b842ce4eb8.jpg

系统自带函数的源文件被预先编译了库 编译器默认链接了该库 所以无需其他配置也看不到实现这些函数的源文件

多文件代码举例

main.c

#include "person.h"//人员相关代码的使用者 也需要包含头文件person.h

int main()
{
	person p;
	p = newperson();
	printperson(&p);
	return 0;
}
person.c

//将一系列人员相关的函数聚合到一个单独的文件person.c中
#include<stdio.h>
#include"person.h"//文件person.c 需要头文件person.h 中的声明或定义 因此文件person.c需要包含文件 person.h 

person newperson()
{
	person p;
	printf("sizeof person in person.c %d", sizeof(person));

	printf("intput name (No more than %d letters): ",name_length);
	scanf("%s", p.name);

	printf("input gender ( 1.male 2.female) : "); 
	scanf("%d", &p.gender);

	printf("input gender ( 1.male 2.female) : ");
	scanf("%d", &p.gender);

	printf("intput weight: ");
	scanf("%1f", &p.weight);

	return p;
}

void printPerson(const person* p) 
{
	printf(" \nname \tgender\theight\tweight\n");
	printf("%s\t%d \t%.2f\t%.2f\n", p->name,p->gender,p->height,p->weight);
}
person.h

//符号常量 宏函数 函数声明 结构声明 类型定义 放置于对应的头文件person.h 中
#define name_length 20//方便修改名字长度限制

typedef struct {
	char name[name_length + 1];
	int gender;
	double height;
	double weight;
}person;

person newperson();
void printperson(const person* p);

总结

  1. 源文件person.c :函数定义。
  2. 头文件 person.h:符号常量、函数宏、函数声明、结构声明、类型定义。
  3. 源文件 person.c需要头文件 person.h中的声明或定义。因此,需要在源文件中#include"person.h"
  4. 使用者,例如文件main.c。包含头文件person.h后,即可使用头文件中的声明或定义以及调用头文件中声明过的函数。

头文件守卫

如果使用了两个头文件 并且两个头文件有交集 就会出现标识符重定义而编译失败

条件编译

可以借助条件编译实现同一个头文件只使用一次的目标

person.h

#ifndef PERSON_H  
#define PERSON_H

...
头文件代码
...

#endif

预处理指令#ifndef用于测试其后跟着的宏是否没有被定义。
若没有被定义 则保留从#ifndef到#endif之间的代码 若被定义 则删除从#ifndef到#endif之间的代码

#pragma once指令

如果你的编译器支持#pragma once 指令 也可以在头文件首部使用#pragma once 达到头文件守卫的效果

person.h 

#pragma once

...
头文件代码
...