宏定义是比较常用的预处理指令,即使用“标识符”来表示“替换列表”中的内容。标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。

常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。

无参宏定义

无参数宏定义的格式为:

#define 标识符 替换列表

替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使用标识符表示一常量,或称符号常量。

说明:

1) # 可以不在行首,但只允许它前面有空格符。例如:

#define PI 3.1416 //正确,该行#前允许有空格

int a;#define N 5 //错误,该行#前不允许有空格外的其他字符

2) 标识符和替换列表之间不能加赋值号 =,替换列表后不能加分号

#define N =5 //虽语法正确,但预处理器会把N替换成=5

int a[N]; //错误,因为宏替换之后为 int a[=5];

宏定义不是语句,是预处理指令,故结尾不加分号。如果不小心添加了分号,虽然有时该宏定义没问题,但在宏替换时,可能导致 C 语法错误,或得不到预期结果。例如:

#define N 5; //虽语法正确,但会把N替换成5;

int a[N]; //语法错误,宏替换后,为int a[5;];错误

3) 由于宏定义仅是做简单的文本替换,故替换列表中如有表达式,必须把该表达式用括号括起来,否则可能会出现逻辑上的“错误”。例如:

#define N 3+2

int r=N*N;

宏替换后为:

int r=3+2*3+2; //r=11

如果采用如下形式的宏定义:

#define N (3+2)

int r=N*N;

则宏替换后,为:

int r=(3+2)*(3+2); //r=25

4) 当替换列表一行写不下时,可以使用反斜线\作为续行符延续到下一行。例如:

#define USA "The United \

States of \

America"

该宏定义中替换列表为字符串常量,如果该串较长,或为了使替换列表的结构更清晰,可使用续行符 \ 把该串分若干行来写,除最后一行外,每行行尾都必须加续行符 \。

如果调用 printf 函数,以串的形式输出该符号常量,即:

printf("%s\n",USA);

则输出结果为:The United States of America

注意:续行符后直接按回车键换行,不能含有包括空格在内的任何字符,否则是错误的宏定义形式。

带参宏定义

带参数的宏定义格式为:

#define 标识符(参数1,参数2,...,参数n) 替换列表

例如,求两个参数中最大值的带参宏定义为:

#define MAX(a,b) ((a)>(b)?(a) : (b))

当有如下语句时:

int c=MAX(5,3);

预处理器会将带参数的宏替换成如下形式:

int c=((5)>(3)?(5) : (3));

故计算结果c=5。

删除宏定义的格式为:

#undef 标识符

说明:

1) 标识符与参数表的左括号之间不能有空格,否则预处理器会把该宏理解为普通的无参宏定义,故以下是错误的带参宏定义形式。

#define MAX (a,b) ( (a) > (b) ? (a) : (b) ) //错误的带参宏定义格式

2) 宏替换列表中每个参数及整个替换列表,都必须用一对小括号 () 括起来,否则可能会出现歧义。

【例 1】以下程序试图定义求两个参数乘积的宏定义,欲使用该宏求 3 与 6 的乘积,分析该程序能否实现预期功能,如果不能,请给出修改方案。

#include

#define MUL(a,b) (a*b)

int main (void)

{

int c;

c=MUL(3,5+1);

printf("c=%d\n",c);

return 0;

}

分析:

1) 由于该宏定义中的替换列表中的参数没有加括号,故宏调用时,如果参数是个表达式,可能会出现歧义,得不到预期结果。

本例中宏调用 c=MUL(3,5+1); 会替换成 c=(3*5+1)=16;,与预期功能不符。

2) 虽然把宏调用时的参数 5+1 括起来,可达到题目要求的效果,但这属于治标不治本。为统一编程规范,把替换列表中的每个参数均加括号,整个替换列表也加括号。

同时,为达到标本兼治,在宏定义时,除单一值参数外,应显式加括号。

修改代码为:

#include

#define MUL(a,b) ((a)*(b))//修改处1

int main (void)

{

int c;

c=MUL(3,(5+1);//修改处2

printf("c=%d\n",c);

return 0;

}

带参宏定义 VS 函教调用

接下来将从调用发生时间、参数类型检查、参数是否需要空间、运行速度等几个主要方面进行对比分析带参宏定义与函数调用的差异。

调用发生的时间

在源程序进行编译之前,即预处理阶段进行宏替换;而函数调用则发生在程序运行期间。

参数类型检查

函数参数类型检查严格。程序在编译阶段,需要检查实参与形参个数是否相等及类型是否匹配或兼容,若参数个数不相同或类型不兼容,则会编译不通过。

在预处理阶段,对带参宏调用中的参数不做检查。即宏定义时不需要指定参数类型,既可以认为这是宏的优点,即适用于多种数据类型,又可以认为这是宏的一个缺点,即类型不安全。故在宏调用时,需要程序设计者自行确保宏调用参数的类型正确。

参数是否需要空间

函数调用时,需要为形参分配空间,并把实参的值复制一份赋给形参分配的空间中。而宏替换,仅是简单的文本替换,且替换完就把宏名对应标识符删除掉,即不需要分配空间。

执行速度

函数在编译阶段需要检查参数个数是否相同、类型等是否匹配等多个语法,而宏替换仅 是简单文本替换,不做任何语法或逻辑检查。

函数在运行阶段参数需入栈和出栈操作,速度相对较慢。

代码长度

由于宏替换是文本替换,即如果需替换的文本较长,则替换后会影响代码长度;而函数不会影响代码长度。

故使用较频繁且代码量较小的功能,一般采用宏定义的形式,比采用函数形式更合适。前面章节频繁使用的 getchar(),准确地说,是宏而非函数。

为了使该宏调用像函数调用,故把该宏设计成了带参数的宏定义:

#define getchar() getc(stdin)

故调用该宏时,需要加括号,即传空参数:getchar()。

python宏定义_宏定义(无参宏定义和带参宏定义),C语言宏定义详解相关推荐

  1. python二分法查找算法_顺序查找算法和折半(二分法)查找算法,C语言查找算法详解...

    查找是指在大量的信息中寻找一个特定的信息.在计算机中,查找是非常重要的一个应用,比如"百度".查找算法的好坏直接影响查找的速度. 常用的查找算法主要有顺序查找和折半(二分法)查找: ...

  2. python定义字符串数组初始化_字符数组及其定义和初始化,C语言字符数组详解...

    字符数组及其定义和初始化,C语言字符数组详解 字符串的存储方式有字符数组和字符指针,我们先来看看字符数组. 因为字符串是由多个字符组成的序列,所以要想存储一个字符串,可以先把它拆成一个个字符,然后分别 ...

  3. C语言结构体详解(结构体定义,使用,结构体大小等)

    c语言结构体详解 1.c语言结构体 1.1 结构体基础知识 1.2 结构体声明 1.3 结构体特殊声明 1.4 结构体的自引用 1.5 结构体的大小的计算 1.5.1了解结构体大小计算规则 1.5.2 ...

  4. python语言的格式框架_django框架模板语言使用方法详解

    本文实例讲述了django框架模板语言使用方法.分享给大家供大家参考,具体如下: 模板功能 作用:生成html界面内容,模版致力于界面如何显示,而不是程序逻辑.模板不仅仅是一个html文件,还包括了页 ...

  5. thymeleaf的初次使用(带参请求以及调用带参js方法)

    thymeleaf的初次使用(带参请求以及调用带参js方法) 之前对于前端框架接触较少,第一次接触thymeleaf,虽说看起来并不复杂但我还是花费了好一会儿才弄懂. 话不多少下面就简单说一下我在项目 ...

  6. [Python从零到壹] 六十四.图像识别及经典案例篇之图像傅里叶变换和傅里叶逆变换详解

    祝大家新年快乐,阖家幸福,健康快乐! 欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所 ...

  7. python自动解析json_Python语言解析JSON详解

    本文主要向大家介绍了Python语言解析JSON详解,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. JSON 函数使用 JSON 函数需要导入 json 库:import jso ...

  8. python request.post 字典参数以json_Python requests.post方法中data与json参数区别详解

    在通过requests.post()进行POST请求时,传入报文的参数有两个,一个是data,一个是json. data与json既可以是str类型,也可以是dict类型. 区别: 1.不管json是 ...

  9. python基础-变量,变量类型,字符串str,元组tuple,列表list,字典dict操作详解(超详细)

    python基础--变量 (文章较长,若需要单独看某一个点,可点击目录直接跳转) 文章目录 python基础--变量 1. 变量 2. 变量类型 2.1数字类型 2.2 字符串 2.3 列表 2.4 ...

  10. python的装饰器迭代器与生成器_python3 装饰器、列表生成器、迭代器、内置方法详解等(第四周)...

    前言: 为什么要学习python3? 原因: 1.学习一门语言能力 2.通过该语言能力完成测试自动化以及独立完成自测框架知识 那么我要做什么呢? 1.每天花十个小时完成python3的学习 要在什么地 ...

最新文章

  1. 北大开源了 Python 中文分词工具包,准确度远超 Jieba
  2. 【Ansible 文档】【译文】模式
  3. win7 VS2013 新建工程 编译lua5.2.0 静态库
  4. VueX的store的简单使用心结
  5. 2014年ENVI/IDL遥感应用与开发培训班-11月重庆站 開始报名了
  6. 简单的机器学习程序_人体动作识别小程序【机器学习 人工智能】
  7. 【洛谷2624】[HNOI2008] 明明的烦恼(Python+利用prufer序列结论求解)
  8. 编辑器未包含main类型_利用 ONLYOFFICE 将在线文档编辑器集成到 Python Web 应用程序中...
  9. 集合之HashSet
  10. NYOJ题目252-01串(动态规划)
  11. java中跑批指的是什么_DB2几个问题的处理(三)
  12. java连接数据库配置文件
  13. 深度学习教程(9) | AI应用实践策略(下)(吴恩达·完整版)
  14. 二进制 八进制 十进制 转换
  15. 这20年我做了些什么
  16. 【算法与数据结构】分治(Divid Conquer)算法——以快排,归并排序,二分查找为例
  17. 从少儿编程讲讲开发行业的大趋势
  18. Android开机自动循环播放视频
  19. 【文本展开收起】uniapp—实现文本的展开与收起功能
  20. 负载均衡_Maglev

热门文章

  1. FLUKE OTDR光纤断点测试仪OFP2-100-Q特色功能及亮点分析
  2. 强化学习10——迭代学习
  3. 青岛市中小学学籍系统服务器,全国各省市中小学学籍网登录入口
  4. MyScript 手写识别数学公式、图形 自动计算
  5. 手写计算机在线计算,计算器中的神器《MyScript手写计算器》
  6. Dump+mysql导入_mysql mysqldump导入数据库
  7. 网站服务器需不需要装杀毒软件,服务器要不要装杀毒软件
  8. 2020农行研发中心面经
  9. JS+html+css实现的一个小小的贪吃蛇游戏
  10. 20年前的网文:我彷徨在唯物主义和唯心主义之间