c语言关于宏的使用十分频繁。但是宏的使用有利也有弊,与此同时,它还是一个特别容易搞错的地方。正是基于此,它常常成为一些面试会侧重考察的地方。

所谓宏就是 #define 机制包括的一个规定,即允许把参数替换到文本中。它的声明方式:#define name(参数列表) stuff

其中参数列表是一个由逗号分隔的符号列表,对应参数作用于stuff中,相当于宏替换函数;如果没有参数列表,那就是我们平常用得比较多的宏替换变量了。

使用特别要注意问题


1. 分号问题

2. 符号优先级问题

3. 作用域问题

4.使用带副作用的宏参数

宏定义处用了分号

#include <iostream>
using namespace std; #define MAX( l, r) l > r? l: r;
int main()
{
int a = , b=; if( a)
int max = MAX( a,b);// <=> max = a > b? a:b;; 出错!!
else
{
}
return ;
}

符号优先级问题

 #include <iostream>
using namespace std; #define MAX( l, r) l > r? l: r
int main()
{
int a = , b=; int max = MAX( a && , b);//a && 3 > b? a && 3: b 达不到预想结果
cout<<max;
return ;
}

改造:

 #define MAX( l, r) ( (l)> (r)? (l) :(r) )
int main()
{
int a = , b=; int max = MAX( a && , b);//(a && 3) > b? (a && 3) : b
cout<<max;
return ;
}

作用域问题

 #include <iostream>
using namespace std;
//=优先级很低,不用优先级问题
#define SWAP(l, r) {\
int tmp = l;\
l = r; \
r = tmp;}
int main()
{
int a = , b=;
if(a)
SWAP(a, b);
//<=> {.... } ; 此处只允许出现一条语句,但这里产生2条
else
{
}
cout<<a <<" "<<b<<endl;
return ;
}

解决:用do{... }while(0)  解决

 //=优先级很低,不用优先级问题
#define SWAP(l, r) do{\
int tmp = l;\
l = r; \
r = tmp;}while()
int main()
{
int a = , b=;
if(a)
SWAP(a, b); //<=> do{.... }while( 0) ;
else
{
}
cout<<a <<" "<<b<<endl;
return ;
}

带副作用的宏参数

当宏参数在宏定义中出现次数超过一次时,如果这个参数具有副作用,那么当使用这个宏时就出现危险,导致不可预料的结果。副作用就是在表达式求值时出现永久性的效果。比如x++,它可以增加x的值。当下一次执行该表达式时,他将产生一个全新的结果。还是以前面的的MAX函数为例

 #include <iostream>
using namespace std; #define MAX(l, r) ( (l)>(r)? (l): (r))
int main()
{
int a = , b=;
int max = MAX(a++, b++); //<=> (a++)>( b++)? ( a++):( b++);
//结果: 3 2 4
cout<<max<<" "<<a<<" "<<b<<endl;
return ;
}

结果让较小的值a增加了一次,但确让较大的值b增加了2次。这种带副作用宏参数会修改变量的值,使用需格外注意。

宏的优缺点


优点

1. 快! 由于是预处理时期直接宏替换,不用像函数那样来回调用返回,增加额外开销。

2. 由于可以替换变量,修改变量仅需在宏定义处修改,增加了程序的可维护性。

缺点:

1. 没有类型安全的检查。宏和类型是无关的,只要对参数操作合法,它可以使用任何参数类型。

2. 极易出错。  宏参数求值是要依赖于周围表达式的上下文环境。没有合理地加括号,得不到期望的结果; 同时参数每次用于宏定义时,它们都会重新求值,由于这样多次求值,所以让具有副作用的参数可能产生不可预料的结果。

3. 不可调试。由于预处理阶段,直接进行了宏替换,对替换掉的代码无法进行调试检查。

4. 替换插入代码,导致程序代码长度大大加长。

C语——宏小结的更多相关文章

  1. 如何编译和调试Python内核源码?

    目录 写在前面 获取源代码 源代码的组织 windows下编译CPython 调试CPython 小结 参考 博客:blog.shinelee.me | 博客园 | CSDN 写在前面 如果对Pyth ...

  2. 【手摸手,带你搭建前后端分离商城系统】01 搭建基本代码框架、生成一个基本API

    [手摸手,带你搭建前后端分离商城系统]01 搭建基本代码框架.生成一个基本API 通过本教程的学习,将带你从零搭建一个商城系统. 当然,这个商城涵盖了很多流行的知识点和技术核心 我可以学习到什么? S ...

  3. C++ macro(宏)使用小结

    谈起C++中的宏,我们第一个想到的应该就是“#define”,它的基本语法长得像这样: #define macroname(para1, para2, para3, ... ,paran) macro ...

  4. IOS开发,知识点小结,ios开发中经常使用的宏定义总结

    IOS开发,从应用跳转到用浏览器打开网页: [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http:// ...

  5. C语言中do...while(0)用法小结

    在linux内核代码中,经常看到do...while(0)的宏,do...while(0)有很多作用,下面举出几个: 本文地址:http://www.cnblogs.com/archimedes/p/ ...

  6. C语言中,宏和全局变量的区别是什么?

    全局变量 是可以在程序中任何地方使用 而且是可以修改的 宏定义也可以在任何地方使用 但是不能在之后修改 数据类型没有限制的 宏的例子:#define 宏名 宏体 #define PI 3.141592 ...

  7. [转载]C宏定义的小结

    FROM:http://blog.csdn.net/sunboy_2050/article/details/6103530 实现代码实例 程序代码: #include <stdio.h> ...

  8. 【原创】mdk5宏定义的使用小结

    前几天在网上申请了一块芯片为stm32f103cbt6的小板子. 在用keil编程的过程中发现一些小问题,总结如下: 使用mdk5开发,与之前的库函数的引用方式已经改变.不需要在选项的c/c++标签页 ...

  9. 小结:Swift、OC语言中多target在代码中如何区分

    一.对swift工程 经实践,网上的方法都无法成功,后来思考DEBUG宏定义方式,经实测有效,方式如下: 注意:不能把swift flags 小三角折叠后双击设置-DTarget4AppStore, ...

随机推荐

  1. python中对列表的所有操作方法

    列表: names = ['a','b','c','d'] 1.追加:names.append() >>> names.append('e') >>> names ...

  2. mysql删除数据库文件ibdata1后引发的故障

    进行性能测试是发现大量报错: Duplicate entry主键重复 可以看到mysql数据库中已经没有innodb引擎启动信息了 之前发现ibdata1占用了大量硬盘,为了省出空间删除了数据库ibd ...

  3. K最近邻kNN-学习笔记

    # -*- coding: utf-8 -*- """ Created on Thu Jan 24 09:34:32 2019 1. 翼尾花数据 2. 用 KNeighb ...

  4. javascript 类型比较方法

    不要使用new Number().new Boolean().new String()创建包装对象: 用parseInt()或parseFloat()来转换任意类型到number: 用String() ...

  5. 大道至简、大智若愚—GO语言最佳详解实践

      导读:2007年,受够了C++煎熬的Google首席软件工程师Rob Pike纠集Robert Griesemer和Ken Thompson两位牛人,决定创造一种新语言来取代C++, 这就是Gol ...

  6. 在Vue中使用计时器笔记

    在Vue中使用了计时器,一定要记得在生命周期destroyed()里清掉,不然第二次进入这个组件,会出现很大的问题 destroyed () { // (很重要)当跳转到其他页面的时候,要在生命周期的 ...

  7. python 全栈开发,Day52(关于DOM操作的相关案例,JS中的面向对象,定时器,BOM,client、offset、scroll系列)

    昨日作业讲解: 京东购物车 京东购物车效果: 实现原理: 用2个盒子,就可以完整效果. 先让上面的小盒子向下移动1px,此时就出现了压盖效果.小盒子设置z-index压盖大盒子,将小盒子的下边框去掉, ...

  8. Python 检测系统时间,k8s版本,redis集群,etcd,mysql,ceph,kafka

    一.概述 线上有一套k8s集群,部署了很多应用.现在需要对一些基础服务做一些常规检测,比如: 系统时间,要求:k8s的每一个节点的时间,差值上下不超过2秒 k8s版本,要求:k8s的每一个节点的版本必 ...

  9. BZOJ1177 [Apio2009]Oil 二维前缀和 二维前缀最值

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1177 题意概括 在一个n*m的矩阵中,每一个位置一个数字. 现在让你选出3个k*k的矩阵,它们互不 ...

  10. js类型判断-丰富加好用

    一, 自己有时候写一些东西,要做类型判断,还有测试的时候,对于原生的和jQuery中的类型判断,实在不敢恭维,所以就写了一个好用的类型判断,一般情况都够用的. function test(type) ...