一、 预处理
1 .预定义符号
__ FILE __ //进行编译的源文件
__ LINE __ //文件当前的行号
__ DATE __ //文件被编译的日期
__ TIME __ //文件被编译的时间
__ STDC __ //如果编译器遵循ANSI C,其值为1,否则未定义
比如:
printf("file:%s line:%d Date:%s Time:%s\n", __FILE__,
__LINE__,__DATE__,__TIME__);
2. #define
#define name stuff
作用是在编译中的预处理阶段将name
替换成stuff,不进行任何运算
例如:
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
预处理前的代码
3+5是直接替换M
#define M 3+5
int main()
{
int y = 5 * M;
printf("%d\n", y);
return 0;
}
预处理后的代码
int main()
{
int y = 5 * 3 + 5;
printf("%d\n", y);
return 0;
}
3. #define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义
宏(define macro)。
下面是宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
#define SQUARE( x ) x * x
将下面的代码置于程序中
SQUARE( 5 );
得到
5 * 5
用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
4.#define 替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。 注意:
- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
字符串有自动连接功能
#define PRINT(FORMAT, VALUE)\
printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10);
输出结果为the value is 10
只有当字符串作为宏参数的时候才可以把字符串放在字符串中
使用 # ,把一个宏参数变成对应的字符串
int i = 10;
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", i+3);
代码中的 #VALUE 会预处理器处理为:“VALUE” .
最终的输出的结果应该是:
the value of i+3 is 13
##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符
#define ADD_TO_SUM(num, value) \
sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
5.宏和函数的对比
#define定义宏
1.每次使用时,宏代码都会被插入到程序中。除了非常
小的宏之外,程序的长度会大幅度增长。
2.执行速度更快。
3.宏参数的求值是在所有周围表达式的上下文环境里,
除非加上括号,否则邻近操作符的优先级可能会产生
不可预料的后果,所以建议宏在书写的时候多些括号。
4.参数可能被替换到宏体中的多个位置,所以带有副作
用的参数求值可能会产生不可预料的结果。
5.宏的参数与类型无关,只要对参数的操作是合法的,
它就可以使用于任何参数类型。
6.宏是不方便调试的.调试时程序员可见的代码只有宏那一条,编译器中的代码已经替换,调试找不到确切的位置。
7.无法递归
函数
1.函数代码只出现于一个地方;每
次使用这个函数时,都调用那个
地方的同一份代码
2.存在函数的调用和返回的额外开
销,所以运行速度相对慢一些
3.函数参数只在函数调用的时候求
值一次,它的结果值传递给函
数。表达式的求值结果更容易预测。
4.函数参数只在传参的时候求值一
次,结果更容易控制。
5.函数的参数是与类型有关的,如
果参数的类型不同,就需要不同
的函数,即使他们执行的任务是
不同的。
6.函数是可以逐语句调试的
7.函数是可以递归的