编译预处理与宏
- #开头的是编译预处理指令,他会在正式编译以前,将所有定义的预处理的名称换成值,名称只能是单词,值可以是各种东西
- 本质上就是替换,就是给一个常量,语句取个名字,编译器就会把有这个名字的地方原封不动的替换为宏的值
- 它们不是C语言的成分,所以不加分号
- #define来定义一个宏
//用宏定义一个常量
#difine PI 3.1415926
//等效于
const double PI = 3.1415926;
//定义一个语句 注释
#define PI2 2*PI //注释
//反斜杠表示与下一行接起来
#define RPT printf("%f ",PI); \
printf("%f ",PI2)
int mian(void )
{
PRT; //因为这里有了个分号,上面就不需要带分号了
}
//没有值的宏,在语法上也是允许的
#ifndef _NODE_H_
#define _NODE_H_
//预定义的宏,编译器自带的
__LINE__ //当前行号,整数
__FILE__ //当前文件名,字符串,下同
__DATE__
__TIME__
__STDC__ //当要求程序严格遵循ANSIC标准时该标识符被赋值为1,主要是判断我们的程序文件是不是标准C程序。
#define PRT printf("%s",__DATE__);
int main(void)
{
PRT
}
//带参数的宏
//为了避免因为运算优先级带来的问题
//整个宏的值要有括号,任何参数出现的地方要有括号
#difine cube(x) ((x)*(x)*(x))
int i = 1;
cube(i+2); //这里如果x不带括号,就会出问题
//多个参数的宏
#define MIN(a,b) ((a)>(b)?(b):(a))
在gcc中有办法可以看到编译过程的文件
$gcc test.c --save-temps
$ls -l
系统 .c -> .i -> .s ->.o -> .out
.i就是中间文件,此时预处理名称全部被替换成值了
部分宏可以被inline函数替代
大程序结构
#include xx.h
- include “” 现在.c目录所在的文件夹中去找,然后再去编译器指定的目录去找,自己定义的.h文件就用""
- included <> 只会在编译器指定的目录底下去找,适用于包含标准库的头文件。
- indlude不是用来应用库的,它是**做函数声明的,确保你在调用函数时给出的参数是正确的类型。**在xx.h中将要用到的函数的原型声明放进去,include的作用就是把文件里的所有内容原封不动的插入到他所在的那一行。stdio.h中只有printf的声明,printf的源代码在.lib文件夹中
- 将所有公开的函数原型和全局变量放在.h文件中,使得他可以在多个.c文件中共享。
- 定义和声明,定义产生代码,声明不产生代码,全局变量的声明不产生代码
//在test.h中
int max(int a,int b); //函数的声明
extern int a; //变量的声明,告诉编译器,这个变量是个int类型,可以被其他文件调用
-
在全局变量中加上static就使得他成为只能在所在的编译单元中被使用的全局变量。
-
重复声明和头文件结构
truct point{
int x;
int y;
}j;
truct point{
int x;
int y;
}j;
//如果像这样重复引用,当然是错的,两个重名的结构体了
//如果这个定义定义在a.h中
//再定义一个b.h,b.h中include了a.h
//那么我们include a.h,再Include b.h时就会出现像刚刚一样的重复引用
//所以加入保护,让头文件只被调用一次
#ifndef __A_H__
#define __A_H__
#endif