目录
1.预定义符号
__FILE__
__LINE__
__DATE__
__TIME__
__STDC__//进行编译的源文件
//文件当前的行号
//文件被编译的日期
//文件被编译的时间
//如果编译器遵循ANSI C,其值为1,否则未定义这些预定义符号都是语言内置的。
直接应用就可以,例子:
2.#define
2.1#define定义标识符
这里使用#define定义一个标识符M(注意空格)为50,打印M时,M直接会替换成50 。
#define定义标识符在预处理阶段,在程序运行之前,因此定义时应在头文件之前。
且预处理中,标识符会直接替换成空格后的元素,是直接替换,要注意括号的使用,例如:
2.2#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
实现举例:
这里建议在写宏的内容时,加上相应的括号,如((x)>(y)?(x):(y)),因为我们的目的是为了求两个参数的最大值,宏的文本替换不加括号存在优先级的问题,就像上述标识符一样,为了确保效率和安全性,要注意宏的内容加上相应的括号。
2.3#define替换规则
1. 在调用宏时,看看参数是否包含任何由#define定义的符号。若有,则被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
2.4#和##
#的用法
把一个宏参数变成对应的字符串
如何把参数插入到字符串中?
例如:那么这里就可以用到宏加 # 来实现:
在PRINT 中,ab会替换n,也会替换 #n,这里要注意 #n 前后都是字符串
代码如下:
#define PRINT(n) printf("the value of "#n" is %d\n",n) #include <stdio.h> int main() { int a = 20; PRINT(a); int b = 50; PRINT(b); return 0; }
##的用法
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。#define CAT(B,a) B##a #include <stdio.h> int main() { int Bossage = 50; printf("%d\n",CAT(Boss,age)); return 0; }
2.5带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
举个例子:
副作用会带来永久性影响:
#define MAX(x,y) ((x)>(y)?(x):(y)) #include <stdio.h> int main() { int a = 4; int b = 6; int max = MAX(a++,b++); printf("%d\n",max); printf("%d %d\n",a,b); return 0; }
3.宏和函数的对比
宏通常被应用于执行简单的运算。函数用来执行较为复杂的运算。
宏不能被调试,并且参数无类型,但执行更快
函数运行操作:函数调用+逻辑运算+函数返回
宏的操作:逻辑运算
具体区别如下:
属 性 | #define定义宏 | 函数 |
代 码 长 度 | 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码 |
执 行 速 度 | 更快 | 存在函数的调用和返回的额外开销,所以相对慢一些 |
操 作 符 优 先 级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 |
带 有 副 作 用 的 参 数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候求值一次,结果更容易控制。 |
参 数 类 型 | 宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。 | 函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是 不同的。 |
调 试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递 归 | 宏是不能递归的 | 函数是可以递归的 |
命名约定:
函数和宏的语法相似,为了便于区别,建议:
把宏名全部大写
函数名不要全部大写
4.#undef
用于移除一个宏定义
#define MAX(x,y) ((x)>(y)?(x):(y)) #include <stdio.h> int main() { int a = 4; int b = 6; #undef MAX int max = MAX(a, b); printf("%d\n", max); return 0; }
移除后的宏无法被解析。
5.条件编译
5.1#if...#endif
#if 常量表达式
#endif
![]()
5. 2多分支条件编译
#if 常量表达式 //... #elif 常量表达式 (可理解为#else if) //... #else //... #endif
用法跟if ...else 语句相似,只是应用在了宏上,常量表达式是对宏的条件。
5. 3判断是否被编译
#if defined(symbol)
#ifdef symbol上两行效果相同,下两行效果相同
#if !defined(symbol)
#ifndef symbol都与#endif成对使用
5.4嵌套指令
将这些指令当作if else 语句使用就可以,这里给出语法模板:
#if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif #elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif
6.头文件的包含
6.1防止头文件重复包含
防止头文件重复包含,可以在声明头文件时加上以下预处理语句:
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__或者
#pragma once//头文件内容
6.2本地文件和库文件本地文件就是自己创建的函数包含时用"文件名":#include "文件名"
库文件包含时用<.h>:#include <stdio.h>
查找策略不同
本地文件:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件,如果找不到就提示编译错误。
库文件:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
注:包含库文件也可用" ",这样做查找的效率就低些,也不容易区分是库文件还是本地文件了,不建议这样做。