回忆:#define的用法
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的用法的更多相关文章
- typedef和#define的用法与区别
typedef和#define的用法与区别 typedef和#define的用法与区别 一.typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程 ...
- C语言中#define的用法(转)
转自:http://www.dingge.com/main/article.asp?id=10 今天整理了一些#define的用法,与大家共享! 1.简单的define定义 #define MAXTI ...
- define的用法
define的用法小结 define的用法只是一种纯粹的替换功能,宏定义的替换是预处理器处理的替换. 一:简单的宏定义用法 格式:#define 标识符 替换内容 替换的内容可以是数字,字符,字符串, ...
- C语言中#define的用法
今天整理了一些#define的用法,与大家共享! 1.简单的define定义 #define MAXTIME 1000 一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写 if(i& ...
- 【转】typedef和#define的用法与区别
typedef和#define的用法与区别 一.typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像: ...
- c++define的用法
c++define的用法 在写程序时经常会碰到这样一个问题,我们需要重复写很多相同的代码,并且这些代码结构相同.总是想自己把这段代码封装一下然后直接进行调用,但是如果这段代码逻辑并不复杂,并且代码 ...
- (转)typedef和#define的用法与区别
typedef和#define的用法与区别 一.typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像: ...
- c++ define的用法(转)
#define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用 ...
- define的用法与注意事项
------------------------------------------------- 在编程使用宏替换时,当字符串中不只一个符号时,加上括号表现出优先级, 如果是带参数的宏定义,则要给宏 ...
随机推荐
- 四则运算安卓客户端UI截图(部分)
1.我们组安卓手机客户端UI设计主要由林培文同学负责,界面中用到的素材全部由他一人用PS制作,所以在素材来源上当属原创啦.正因为UI由一个人设计,同时他还得分担少量后台代码的编写,颇多的工作量与人才短 ...
- 阿里巴巴分布式服务框架 Dubbo 团队成员梁飞专访
项目主页:http://alibaba.github.io/dubbo-doc-static/Home-zh.htm 我的博客:http://javatar.iteye.com
- 手机CPU和GPU厂商
CPU: 1.苹果 (Apple) A系列 ARM授权,基于Cortex-A系列架构 A5基于Cortex-A9架构,双核,主频800M-1Ghz,内存双通道32bitLPDDR2,GPU采用Powe ...
- RCF进程间通信Demo程序
在上一篇文章RPC通信框架--RCF介绍中,介绍了RCF的优点,本篇文章从头开始演示如何用RCF编写一个跨进程通信的Demo程序. 将RCF编译为静态库 从官网下载到的源码中包含一个RCF的项目,但是 ...
- Docker:Ubuntu下的安装
Docker是什么 Docker 是 Docker.Inc 公司开源的一个基于 LXC技术之上构建的Container容器引擎, 源代码托 管在 GitHub 上, 基于Go语言并遵从Apache2. ...
- 《Entity Framework 6 Recipes》中文翻译系列 (17) -----第三章 查询之分页、过滤和使用DateTime中的日期部分分组
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-12 分页和过滤 问题 你想使用分页和过滤来创建查询. 解决方案 假设你有如图3 ...
- 有关bootstrap之排版
1.标题 HTML中的所有标题标签,从<h1> 到 <h6> 均可用.另外,还提供了.h1 到.h6 class,为的是给inline属性的文本赋予标题的样式. 在标题内还可以 ...
- JS中script词法分析
核心:JS中的script是分段执行的. <script> var i = 10; </script> <script> alert(i); </script ...
- tornado 学习笔记9 Tornado web 框架---模板(template)功能分析
Tornado模板系统是将模板编译成Python代码. 最基本的使用方式: t = template.Template("<html>{{ myv ...
- MFC的定时器OnTimer
本文总结来源出自鸡啄米,感谢鸡啄米.来源:http://www.jizhuomi.com/software/232.html 定时器简介 定时器,可以帮助开发者或者用户定时完成某项任务.在使用定时器时 ...