C语言的头文件和宏定义详解
头文件
头文件,顾名思义就是定义在C语言文件头部的那一坨东西
#include <stdio.h>
这就是一个标准输入输出的头文件声明,头文件声明写在定义文件(后缀名.c的文件)的头部,并在定义文件中具体实现
#include <stdlib.h>
#include "mylib.h"
这是两种声明头文件的方法,其中尖括号表示“到环境指定的目录去引用”,而双引号表示“首先在当前目录查找,然后在到环境指定的目录去引用”
在C表准库中每个库函数都在一个头文件中声明,可以通过第一种方式引用
头文件的格式
#ifndef _MYLIB_H_
#define _MYLIB_H_
...
#endif
第一句“ifndef”意思是“如果在导入头文件的文件中之前没有导入该头文件就编译下面的代码”,该句的作用是防止重复导入
第二句“define”是“宏定义”的意思,表示以下代码是的头文件主体部分
最后来一句“endif”和“ifdef”首尾呼应
其中“ifndef”和“define”后面跟的是相同的“标识”,通常和头文件名相同,所有字母均大写并把点号改为下划线即可
#include "mylib.h"
看到这句话后编译器会把该头文件“mylib.h”复制粘贴到导入的文件中,之后你就可以使用头文件中定义的常量和结构定义了
显然恰当地使用头文件有利于更好的组织文件和项目
提请注意
1. 头文件只是声明,不占内存;在编译时会被合并到源文件
2. 头文件和其它C语言文件一样可以引用其它文件,可以写预处理块但是不要出现具体语句
3. 可以在头文件中定义宏函数,其本质上还是一个声明
4. 各个头文件相互独立,标准头文件之间不存在相互包含关系
5. 头文件可以重复引用,但是相当于只导入一次
6. 从C语法角度讲完全可以在头文件中写任何东西,因为#include
在作用上和Ctrl-C + Ctrl-V
等效——但是这样时不推荐的;头文件的作用就是更好的组织代码
何时使用
1. 结构体的定义
2. 函数声明,变量声明,宏定义,常数定义等
3. 当源代码不便公布时使用头文件提供接口
4. 在大项目中需要多文件联合编译
小栗子
#ifndef _NODE_H_
#define _NODE_H_
typedef struct _node{
int value;
struct _node *next;
}Node;
#endif
#include "node.h"
int main(int argc, char const argv[])
{
Node *p=(Node*)malloc(sizeof(Node));
...
return 0;
}
常用头文件
stdio.h 标准输入输出
stdlib.h 标准常用库
string.h 字符串函数库
math.h 数学库
ctype.h 字符函数库
time.h 时间库
windows.h 微软视窗库
宏定义
宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。宏定义和操作符的区别是:宏定义是替换,不做计算,也不做表达式求解。
“宏定义”也称“宏替换”,“宏”
#define PI 3.1415926
这就是一个简单的宏,在程序的预处理阶段宏名会被替换为后面的字符串
传入参数的宏
1. #:字符串化操作,即将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串,使用时置于宏定义体中的参数名前,如:
#define func(para) #para
...
char str[]=func(hello); //被展开为:char str[]="hello"
说明:如果传入的参数之前有空格则忽略之,如果参数之间有多个空格则在连成字符串时只算一个
2. #@:字符化操作,即将宏定义传入的参数名转换为用一对单引号扩起来的参数名字符串,使用时置于参数名前,如:
#define fun(pa) #@pa
char a=fun(a); //被展开为char a='a';
3. ##:参数连接操作,即将宏定义的多个形参连接成一个实际参数,如:
#define COMMAND(a,b) a##b
...
COMMAND(1,2); //相当于12
CMOOAND(ac,b); //相当于acb
4. \:当前行继续操作,实际上是对换行符转义,如:
#define LOOP(FROM, TO, CONTENT)\
for(int i=FROM;i<TO;i++){\
CONTENT\
}
5. _VA_ARGS_:变长参数操作,即可以接受不定个数的参数,如:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
eprintf ("%s:%d: ", input_file, lineno)
//==> fprintf (stderr, "%s:%d: ", input_file, lineno)
为什么要使用宏定义
简而言之,使用宏定义可以提高代码的可读性
具体的说,可以减少magic number的使用,并且可以做一些方便的替换,如下面的代码:
#define MALLOC(n, type) (type*)malloc((n)*sizeof(type))
使用时,int *p=MALLOC(10, int);
即可
宏的规范写法
1. 宏名大写
2. 宏定义语句末尾不加分号
3. 宏函数的形式参数不加类型
另外宏定义需要注意的
1. 宏定义可以嵌套
2. 宏定义不能出现在字符串的“”中
3. 宏定义不分配内存,变量定义才分配内存
4. 宏定义只是简单的替换,而且是直接对源码的字符串替换,如下面的宏定义就不能很好的表达求平方函数:
#define sqrt(x) x*x
...
int y=sqrt(1+2); //y = 1+2*1+2 = 5 ≠9
这时候加上括号就好了:
#define sqrt(x) (x)*(x)
...
int y=sqrt(1+2); //y = (1+2)*(1+2) = 9
5. 宏函数和自定义函数相比,效率更高但是安全性低且会使编译生成的目标文件变大;宏函数没有分配和释放栈帧、传参、传返回值等一系列工作,适合那些简短并且频繁调用的函数,但是对于递归则不推荐使用宏
https://blog.csdn.net/abc_12366/article/details/79155540上一篇: shell命令:sed命令