c语言-零基础学习第二十七讲-预处理详解

1.预定义符号:

2.#define定义常量:

#define M 100
int main()
{
	int a = M;
	printf("%d", a);//100
	return 0;
}

注:#define后面不要加‘ ;’。

3.#define定义宏:

#define plus(a) a*a
int main()
{
	int p = plus(3+2);
	printf("%d", p);//11
	return 0;
}

注:如上,替换为3+2*3+2=11

如想独立计算,可

#define plus(a) ((a)*(a))

注:“\”:续行符,替换回车,可转行。

4. 带有副作用的宏参数:

当宏参数在宏的定义中出现超过⼀次的时候,如果参数带有副作⽤,那么你在使⽤这个宏的时候就可能出现危险,导致不可预测的后果。副作⽤就是表达式求值的时候出现的永久性效果。

eg:

#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
	int a = 15, b = 9;
	int m = MAX(a++, b++);//m=16,a=17,b=10
	//m=((a++)>(b++)?(a++):(b++))
	return 0;
}

5.宏替换的规则:

1. 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注:

1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。

2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
eg:#define m 100
printf(“m=%d”,m);--->printf(“m=%d”,100);

6.宏与函数的对比:

(1)宏的优点:

1. ⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐函数在程序的规模和速度⽅⾯更胜⼀筹

2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之这个宏怎可以适⽤于整形、⻓整型、浮点型等可以⽤于 > 来⽐较的类型。宏是类型⽆关的

3.宏能做到函数做不到的事情:如,宏的参数可以出现类型,但是函数做不到

#define MALLOC(num,type) (type*)malloc(num,sizeof(type))
...
//使⽤
MALLOC(10, int); //类型作为参数
//预处理器替换之后:
(int*)malloc(10,sizeof(int));

(2)宏的缺点:

1. 每次使⽤宏的时候,⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短,否则可能⼤幅度增加程序的⻓度。
2. 宏是没法调试的。
3. 宏由于类型⽆关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程序容易出现错。

总结:逻辑简单用宏,逻辑复杂用函数。

7. #和##:

(1) #运算符:

#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。

#运算符所执⾏的操作可以理解为”字符串化“。

eg:

#define PRINT(n) printf("the value of "#n " is %d", n);

其中#n会被替换成“n”,会转换为真实的值。

(2)##运算符:

 ## 可以把位于它两边的符号合成⼀个符号,这样的连接必须产⽣⼀个合法的标识符。

#define GENERIC_MAX(type)\
type type##max(type x,type y)\
{\
return x>y?x:y;\
}
GENERIC_MAX(int)
GENERIC_MAX(float)//生成两个函数

8. 命名约定:

⼀般来讲函数的宏的使⽤语法很相似。所以语⾔本⾝没法帮我们区分⼆者。

那我们平时的⼀个习惯是:

把宏名全部⼤写,函数名不要全部⼤写。

9. #undef:移除宏定义:

#define M 100
int main()
{
	int a = M;//可替换
#undef M;
	int b = M;//不可替换
	return 0;
}

10. 命令行定义:

(VS不支持)

许多C的编译器提供了⼀种能⼒,允许在命令⾏中定义符号。⽤于启动编译过程。

//linux 环境演⽰
gcc -D ARRAY_SIZE=10 programe.c

11. 条件编译:

满足条件就编译,不满足就放弃编译。

(1)

#if 常量表达式
//...
#endif

(2)多个分⽀的条件编译:

#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif

(3)判断是否被定义:

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol

(4)嵌套指令
 

#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

12. 头⽂件的包含:

12.1 头⽂件被包含的⽅式:

12.1.1 本地⽂件包含
#include "filename"

查找策略:先在源⽂件所在⽬录下查找,如果该头⽂件未找到,编译器就像查找库函数头⽂件⼀样在标准位置查找头⽂件。如果找不到就提⽰编译错误。

12.1.2 库⽂件包含
#include <filename.h>

查找头⽂件直接去标准路径下去查找,如果找不到就提⽰编译错误。

12.2 嵌套⽂件包含:

#include 指令可以使另外⼀个⽂件被编译。如果重复包含,对编译的压⼒就⽐较⼤。

如何解决头⽂件被重复引⼊的问题?答案:条件编译。

#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容
#endif //__TEST_H__

或者:

#pragma once

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值