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. 【Beta】Daily Scrum Meeting第七次

    1.任务进度 学号 已完成 接下去要做 502 发布任务到服务器 测试 509 将各api的处理逻辑放到类里面 让主api调用这些类 517 删除任务和教师的控件及逻辑 提交报课审核信息 530 完善 ...

  2. [LintCode] Find Peak Element 求数组的峰值

    There is an integer array which has the following features: The numbers in adjacent positions are di ...

  3. Odoo 二次开发教程(三)-第一个Model及Form、Tree视图

    创建完我们的模块,接下来我们就要为我们的模块添加一些对象.今天我们将要创建一个学生对象(tech.student)和一些基本的属性,并将用form和tree视图将其展示出来: 一. 创建tech.st ...

  4. mailto

    什么是mailto链接? mailto链接是一种html链接,能够设置你电脑中邮件的默认发送信息.但是需要你电脑中安装默认的E-mail软件,类似Microsoft Outlook等等.加入您已经安装 ...

  5. 利用sqlmap进行mysql提权的小方法(win与liunx通用)

    文章作者:pt007@vip.sina.com文章来源:https://www.t00ls.net/thread-36196-1-1.html1.连接mysql数据打开一个交互shell:sqlmap ...

  6. C\C++ 1A2B小游戏源码

    学了一段时间,心血来潮写了一个1A2B小游戏,很多人应该玩过,是一个挺有意思的益智小游戏,之前用易语言写过,现在又用C++重写了一下. 编译运行无错,整体程序设计思路为:进入循环,初始化游戏,读入一个 ...

  7. 安卓学习之--UI控件用法 单选 按钮 下拉框

    1.单选 .RadioGroup 可将各自不同的RadioButton ,设限于同一个Radio 按钮组,同一个RadioGroup 组里的按钮,只能做出单一选择(单选题). <RadioGro ...

  8. 转:隐马尔可夫模型(HMM)攻略

    隐马尔可夫模型 (Hidden Markov Model,HMM) 最初由 L. E. Baum 和其它一些学者发表在一系列的统计学论文中,随后在语言识别,自然语言处理以及生物信息等领域体现了很大的价 ...

  9. SQL Server无法收缩日志文件 2 因为逻辑日志文件的总数不能少于 2问题

    SQL Server无法收缩日志文件 2 因为逻辑日志文件的总数不能少于 2问题 最近服务器执行收缩日志文件大小的job老是报错 我所用的一个批量收缩日志脚本 USE [master] GO /*** ...

  10. 【整理】待毕业.Net码农就业求职储备

    声明:本文题目来源于互联网,仅供即将从学校毕业的.Net码农(当然,我本人也是菜逼一个)学习之用.当然,学习了这些题目不一定会拿到offer,但是针对就业求职做些针对性的准备也是不错的.此外,除了技术 ...