1 .#define 定义标识符
定义的内容可以是一个整型,一个字符,一串字符串,一段代码。
#define MAX 1000#define WH abcd#define do_forever for(;;) // 用更形象的符号来替换一种实现#define CASE break;case // 在写 case 语句的时候自动把 break 写上。
重点以最后的 #define CASE break;case举例
#include<stdio.h>
#define CASE break;case
//int main()
//{
// int x = 0;
// switch (x)
// {
// case 1:
// break;
// case 2:
// break;
// case 3:
//
// }
// return 0;
//}
int main()
{
int x = 0;
switch (x)
{
case 1:
CASE 2:
CASE 3:
}
return 0;
}
提问: 在define定义标识符的时候,要不要在最后加上 ; ?
比如:
#define MAX 1000;#define MAX 1000
其实并没有强行规定,你只要知道 #define的机制是一个直接替换的机制,如果加了 ; 则替换的时候也要加上 ; 。
2. #define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏( macro )或定义 宏(define macro )。
下面是宏的申明方式:
#define name( parament - list ) stuff
其中的
parament
-
list
是一个由逗号隔开的符号表,它们可能出现在
stuff
中。
注意:
参数列表的左括号必须与 name 紧邻。 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
例子:
#define MAX(x,y) ((x)>(y)?(x):(y))
宏的书写要规范:
合理范围内不要吝啬括号。
例1:
有#define SQUARE( x ) x * x则SQUARE(4+1)等于多少?是25吗?
答案是9。
#define的机制是一个直接替换的机制。
SQUARE(4+1)等于 4+1*4+1 等于9
例2:
#define DOUBLE(x) (x) + (x)int a = 5 ;printf ( "%d\n" , 10 * DOUBLE ( a )); 等于多少?是100吗?
答案是55。
同理 10 * DOUBLE(5) 等于 10*5+5 等于55。
3. #和##
3.1#
如何把参数插入到字符串中?
首先我们看看这样的代码:
char* p = "hello ""bit\n" ;printf ( "hello"" bit\n" );printf ( "%s" , p );
这里输出的是不是
hello bit
?
答案是确定的:是。
我们发现字符串是有自动连接的特点的。
当我们想要打印
the value of 变量名 is 具体值类似形式的表达式时。
我们无法有一个通用表达式高效表示,故无法写成函数。
那我们是不是可以写这样的代码?
使用
#
,
把一个宏参数变成对应的字符串
#include<stdio.h>
#define PRINT(n,format) printf("the value of "#n" is "format"\n",n);
int main()
{
int a = 10;
double b = 3.14;
PRINT(a,"%d");
PRINT(b, "%.2lf");
return 0;
}
3.2##
##
的作用
##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
#include<stdio.h>
#define CAT(x,y) x##y
int main()
{
int whabc = 99;
printf("%d", CAT(wh, abc));
return 0;
}
注:
(1)这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。(2)##只能在宏里这样用
4.带副作用的宏参数
带副作用?
如:
x + 1 ; // 不带副作用x ++ ; // 带有副作用
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能 出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
MAX
宏可以证明具有副作用的参数所引起的问题。
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )...x = 5 ;y = 8 ;z = MAX ( x ++ , y ++ );printf ( "x=%d y=%d z=%d\n" , x , y , z ); // 输出的结果是什么?
这里我们得知道预处理器处理之后的结果是什么:
z = ( (x++) > (y++) ? (x++) : (y++));
所以输出的结果是:
x=6 y=10 z=9
5. 宏和函数对比
属
性
|
#define
定义宏
|
函数
|
代 码 长 度
|
每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长。
|
函数代码只出现于一个地方;每 次使用这个函数时,都调用那个 地方的同一份代码
|
执 行 速 度
|
更快
|
存在函数的调用和返回的额外开 销,所以相对慢一些
|
操 作符 优
先 级
|
宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号。
|
函数参数只在函数调用的时候求 值一次,它的结果值传递给函 数。表达式的求值结果更容易预 测
|
带 有
副 作
用 的
参 数
|
参数可能被替换到宏体中的多个位置,所以带有副作 用的参数求值可能会产生不可预料的结果
|
函数参数只在传参的时候求值一 次,结果更容易控制。
|
参 数
类 型
|
宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型。
|
函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是
相同的。
|
调
试
|
宏是不方便调试的
|
函数是可以逐语句调试的
|
递
归
|
宏是不能递归的
|
函数是可以递归的
|
命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:
把宏名全部大写函数名不要全部大写
6. #undef
这条指令用于移除一个宏定义。
#undef NAME// 如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
以上为我个人的小分享,如有问题,欢迎讨论!!!