ANSI C规定:#前可以有空格或者tab,#和指令其余部分之间也可以有空格,可以出现在任何地方,作用域从定义处到文件结尾。

因为预处理开始前,系统会删除反斜线和换行符的组合,故可以把指令扩展到几个物理行,这些物理行组成单个逻辑行。

//每个#define行(指逻辑的行):三部分组成
//指令本身 宏 替换列表(或主体)
#define PI 3.141592653

宏分为类对象宏(代表值的宏)和类函数宏
宏的名字中间不能有空格,必须遵循c命名规则,从宏变成最终的替换文本叫宏展开,预处理器不进行计算,只是简单的文本替换

语言符号类型字符串和字符型字符串

系统把主体当作语言符号类型字符串,而不是字符型字符串,预处理器中的语言符号是宏定义主体里的单独的词,用空白字符把这些词分开。

#define SIX 2*2//定义里有一个语言符号,序列2*2
#define AAA 2 * 2//定义里有三个语言符号,2,*,3

若把主体解释为字符型字符串,预处理器用2    *     2替换AAA,额外的空格也算替换

若把主体解释为语言符号类型字符串,预处理器只用单个空白字符分割的三个语言符号去去替换AAA

2 * 2的空格只是分割主体语言符号的符号,不会都算(注意:不同编译器做法不一样)

重定义常量

开始把AAA定义为常量4,后来在该文件里,又把AAA定义为10,这叫重定义常量,不同编译器策略不一样,不过标准c规定:这是错误的,只允许新定义和旧定义完全相同!

//相同定义意味主体具有相同顺序的语言符号
#define A 2 * 3
#define A 2 * 3//两者等价,都是三个语言符号的主体

下面的定义被ANSI C认为不同

#define A 2*3//只有一个语言符号的主体,可以用#undef指令重新定义宏

在#define里使用参数——类函数宏

类函数宏:外形和作用和函数类似,可以使用参数,也是()括起来。一般要主体参数里都加上小括号来避免文本替换的错误发生。

#define  SQRT(x)  x*x
int main(void)
{
int x = ;
printf("%d\n", SQRT());//
printf("%d\n", SQRT());//
printf("%d\n", SQRT( + x));//
printf("%d\n", SQRT( + ));//没有打印16,而是8=2 + 2 * 2 + 2
//一定主要,类函数宏也是文本替换,可以通过在主体里加圆括号来约束结合性   //避免在宏里使用增量和减量运算符
printf("%d\n", SQRT(++x));//16,++x*++x,x两次增量,一次在乘法前,一次在后
printf("%d\n", x);//4
//不同编译器处理不一样,4*4=16,这里x先两次自增到4,再乘。有的编译器不一样,故宏避免使用增量减量
system("pause");
return ;
}

宏里的#和##运算符

#include <stdio.h>
#include <stdlib.h>
#define PSQR(X) printf("x的平方 = %d\n", ((X) * (X)));//注意这里的;是printf语句的分号
int main(void)
{
PSQR();
system("pause");
return ;
}

结果是 x 的平方 = 9

注意,printf引号里的x被看作是普通的文本,而不是一个可以被替换的语言符号!可以在类函数宏的替换部分,使用#运算符做预处理,这样普通文本转换为可以被替换的语言符号!如果x是宏参数,那么#x可以把参数名转换为相应的字符串处理,就可以输出3,而不是X!(该过程也叫字符串化)。

#define PSQR(X) printf(#X "的平方 = %d\n", ((X) * (X)));
int main(void)
{
PSQR();
system("pause");
return ;
}

3的平方 = 9

调用宏,用 ”3” 代替 #X ,标准C把这些字符串链接起来,产生最终结果。

#只可以用于类函数宏,而##运算符既可以用于类函数宏,也可以用到类对象宏。

##可以把两个语言符号组合为单个语言符号!

#define XNAME(n) X##n //类函数宏里,n是宏参数(一个语言符号),X是一个语言符号,加上##,两者变一个语言符号
#define PRINT_XN(n) printf("x" #n " = %d \n", X ## n);//注意这里X必须大写,因为XNAME里X大写了
int main(void)
{
int XNAME(1) = 14;//相当于 int x1 = 14;
PRINT_XN(1);
system("pause");
return 0;
}

打印 x1 = 14

可变宏

函数可以接收固定参数,也可以接收可变参数(printf函数)。同样宏也是这样。

注意:可变,字符串化是c的词汇,但是固定函数,固定宏,不变宏等不是C的词汇。

实现思想:宏定义的参数列表最后一个参数为省略号…(三个点),预定义宏__VA_ARGS__就可以用在被替换部分,说明省略号的含义。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PR(X, ...) printf("可变宏:" #X __VA_ARGS__ );//注意这里是两个下划线__
int main(void)
{
double x = ;
double y;
y = sqrt(x);
//第一个参量对应宏的X,值为1,则#X 变为1,宏展开后成为:
//printf("可变宏:" "1" "x = %g\n", x);
//然后三个字符串被编译器连接为:
//printf("可变宏:1x = %g\n", x);
//最后指向输出
PR(, "x = %g\n", x);//__VA_ARGS__告诉了省略号代表了什么
//注意:省略号只能代表最后一个参数!!!
system("pause");
return ;
}

宏是用空间换取时间,函数是用时间换取空间,比如,使用宏100次,那么会在程序里插入100次宏代表的代码,浪费空间,但是速度很快。使用函数100次,程序只有一份函数的拷贝,节省空间,但是每次调用函数,都要压栈出栈,返回调用点,浪费时间!

简单函数就能处理的功能,一般用宏,或者内联函数。

宏名字不能有空格,必须大写(约定俗成的,提醒程序员这是宏),宏不检测类型只是简单的文本替换,好处是int类型,或者double类型等都能使用。

用圆括号括起来类函数宏里的每个参数,好习惯,避免文本替换造成逻辑混乱的错误。

欢迎关注

dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

回忆:#define的用法的更多相关文章

  1. typedef和#define的用法与区别

    typedef和#define的用法与区别 typedef和#define的用法与区别 一.typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程 ...

  2. C语言中#define的用法(转)

    转自:http://www.dingge.com/main/article.asp?id=10 今天整理了一些#define的用法,与大家共享! 1.简单的define定义 #define MAXTI ...

  3. define的用法

    define的用法小结 define的用法只是一种纯粹的替换功能,宏定义的替换是预处理器处理的替换. 一:简单的宏定义用法 格式:#define 标识符 替换内容 替换的内容可以是数字,字符,字符串, ...

  4. C语言中#define的用法

    今天整理了一些#define的用法,与大家共享! 1.简单的define定义 #define MAXTIME 1000 一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写 if(i& ...

  5. 【转】typedef和#define的用法与区别

    typedef和#define的用法与区别 一.typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像: ...

  6. c++define的用法

    c++define的用法   在写程序时经常会碰到这样一个问题,我们需要重复写很多相同的代码,并且这些代码结构相同.总是想自己把这段代码封装一下然后直接进行调用,但是如果这段代码逻辑并不复杂,并且代码 ...

  7. (转)typedef和#define的用法与区别

    typedef和#define的用法与区别 一.typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像: ...

  8. c++ define的用法(转)

    #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用 ...

  9. define的用法与注意事项

    ------------------------------------------------- 在编程使用宏替换时,当字符串中不只一个符号时,加上括号表现出优先级, 如果是带参数的宏定义,则要给宏 ...

随机推荐

  1. js数组操作总结

    1.数组的检测 ECMAScript3    if(value instanceof Array){ //执行操作 }    假定单一全局环境,如果网页存在多个框架,多个window,Array具有不 ...

  2. <十三>JDBC_dbcp数据库连接池

    配置文件:jdbc.properties username=rootpassword=kkdriverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql://1 ...

  3. Java 用LinkdeList实现52张扑克牌

    用LinkdeList实现52张扑克牌(不含大小王)的洗牌功能.提示:花色 ,和数字分别用数组存储. import java.util.LinkedList; import java.util.Ran ...

  4. python方法中的self

    前几天在写一个c作业时,突发奇想,在结构体中加入函数指针, 像这样: struct People { int _age; int (*age)(); }people; 这样调用时就可以 people. ...

  5. 初涉深度优先搜索--Java学习笔记(二)

    版权声明: 本文由Faye_Zuo发布于http://www.cnblogs.com/zuofeiyi/, 本文可以被全部的转载或者部分使用,但请注明出处. 上周学习了数组和链表,有点基础了解以后,这 ...

  6. For循环打印正三角,倒三角,菱形

    A:正三角形: 内循环的判断条件:y<=x for(int x=0; x<5; x++) { for(int y=0; y<=x; y++) { System.out.print(& ...

  7. ExtJS扩展:扩展grid之toolbar button禁用表达式

          在前一篇文章我们扩展了grid通过选中记录数来禁用toolbar上的按钮,有时候我们需要通过记录中的数据来决定是否禁用按钮,今天我们就来扩展它.       照例,最新的代码和例子都在gi ...

  8. 一种让 IE6/7/8 支持 media query 响应式设计的方法

    在不同的浏览器宽度下使用不同的 CSS 声明,常见的方案是使用 media query,但这个方案不支持 IE9 以下浏览器. 国外比较流行的 UI 框架 bootstrap v3 版本中使用 med ...

  9. XCode的个人使用经验

    Xcode是强大的IDE(但个人觉得不如Visual Studio做得好),其强大功能无需本人再赘述,本文也不是一篇“快捷键列表”,因为XCode上的快捷键极其多,而且还有不少是需要同时按下四个按键的 ...

  10. twobin博客样式—“蓝白之风”

    自暑假以来,囫囵吞枣一般蒙头栽入前端自学中,且不说是否窥探其道,却不自觉中提高了对网页版面设计的要求,乃至挑剔.一个设计清爽美观的网页能让读者心旷神怡,甚至没有了阅读疲劳:而一个设计粗劣嘈杂的网页实在 ...