带参数的宏定义

不是进行简单的字符串替换,还要进行参数替换。其定义的一般形式为

   #define 宏名(参数表) 字符串

  字符串中包含在括弧中所指定的参数。如:

  #define S(a,b) a*b
  area=S(3,2);

定义矩形面积S,a 和 b 是边长。在程序中用了S(3,2),把3、2分别代替宏定义中的形式参数a、b,即用 3*2 代替 S(3,2)。因此赋值语句展开为

  area=3*2;

  对带参的宏定义是这样展开转换的:在程序中如果有带实参的宏(如S(3,2)),则按 #define 命令行中指定的字符串从左到右进行置换。如果串中包含宏中的形参(如a、b),则将程序语句中相应的实参(可以是常量、变量或表达式)代替形参。如果宏定义中的字符串中的字符不是参数字符(如 a*b 中的 * 号),则保留。这样就形成了置换的字符串,见右图。

【例1】

#define PI 3.1415926
#define S(r) PI*r*r

main(){
   float a,area;
   a=3.6;
   area=S(a);
   printf("r=%f\narea=%f\n",a,area);
}

运行结果如下:

r=3.600000
area=40.715038

  赋值语句"area=S(a)";经宏展开后为

  area=3.1415926*a*a;

  说明:

  (1) 对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替 #define 命令行中的形参。例1中语句中有S(a),在展开时,找到 #define 命令行中的 S(r),将 S(a) 中的实参 a 代替宏定义中的字符串“PI*r*r”中的形参r,得到PI*a*a。这是容易理解而且不会发生什么问题的。但是,如果有以下语句:

  area=S(a+b);

这时把实参 a+b 代替 PI*r*r 中的形参r,成为

  area=PI*a+b*a+b;

请注意在 a+b 外面没有括弧,显然这与程序设计者的原意不符。原意希望得到

  area=PI*(a+b)*(a+b);

为了得到这个结果,应当在定义时,在字符串中的形式参数外面加一个括弧。即

  #define S(r) PI*(r)*(r)

在对 S(a+b) 进行宏展开时,将 a+b 代替 r,就成了

  PI*(a+b)*(a+b)

这就达到了目的。

  (2) 在宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为替代字符串的一部分。例如,如果有

  #define S (r) PI*r*r

被认为 S 是符号常量(不带参的宏名),它代表字符串“(r) PI*r*r”。如果在语句中有

  area=S (a);

则被展开为

  area=(r) PI*r*r (a);

显然不对了。

  有些读者容易把带参数的宏和函数混淆。的确,它们之间有一定类似之处,在调用函数时也是在函数名后的括弧内写实参,也要求实参与形参的数目相等。但是带参的宏定义与函数是不同的。主要有:

  (1) 函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换。例如上面的 S(a+b),在宏展开时并不求 a+b 的值,而只将实参字符“a+b”代替形参r。

  (2) 函数调用是在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。

  (3) 对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换。而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。例如:

  #define CHAR1 CHINA  (字符)
  #define a 3.6     (数值)

CHAR1 和 a 不需要定义类型,它们不是变量,在程序中凡遇 CHAR1 均以 CHINA 代之;凡遇 a 均以 3.6 代之,显然不需定义类型。同样,对带参的宏:

  #define S(r) PI*r*r

r 也不是变量,如果在语句中有 S(3.6),则展开后为 PI*3.6*3.6,语句中并不出现r。当然也不必定义 r 的类型。

  (4) 调用函数只可得到一个返回值,而用宏可以设法得到几个结果。

【例2】

#define PI 3.1415926
#define CIRCLE(R,L,S,V) L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*R

main(){
   float r,l,s,v;
   scanf("%f",&r);
   CIRCLE(r,l,s,v);
   printf("r=%.2f,l=%.2f,s=%.2f,v=%.2f\n",r,l,s,v);
}

经预编译宏展开后的程序如下:

main(){
   float r,l,s,v;
   scanf("%f",&r);
   l=2*3.1415926*r;s=3.1415926*r*r;v=4.0/3.0*3.1415926*r*r*r;
   printf("r=%.2f,l=%.2f,s=%.2f,v=%.2f\n",r,l,s,v);
}

  请注意,实参 r 的值已知,可以从宏带回3个值(l,s,v)。其实,只不过是字符代替而已,将字符r代替R,l代替L,s代替S,v代替V,而并未在宏展开时求出l、s、v的值。

  (5) 使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不使源程序变长。

  (6) 宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。

  一般用宏来代表简短一的表达式比较合适。有些问题,用宏和函数都可以。如:

  #define MAX(x,y) (x)>(y)?(x):(y)
  main(){
    int a,b,c,d,t;
    ┊
    t=MAX(a+b,c+d);
    ┊
  }

  赋值语句展开后为

  t=(a+b)>(c+d)?(a+b):(c+d);

注意:MAX不是函数,这里只有一个 main 函数,在 main 函数中就能求出 t 的值。

  这个问题也可用函数来求:

  int max(int x,int y){
    return(x>y?x:y);
  }

  main(){
    int a,b,c,d,t;
    ┊
    t=max(a+b,c+d);
    ┊
  }

max是函数,在 main 函数中调用 max 函数才能求出 t 的值。

  请仔细分析以上两种方法。

  如果善于利用宏定义,可以实现程序的简化,如事先将程序中的“输出格式”定义好,以减少在输出语句中每次都要写出具体的输出格式的麻烦。

【例3】

#define PR printf
#define NL "\n"
#define D "%d"
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"

main(){
   int a,b,c,d;
   char string[]="CHINA";
   a=1;b=2;c=3;d=4;
   PR(D1,a);
   PR(D2,a,b);
   PR(D3,a,b,c);
   PR(D4,a,b,c,d);
   PR(S,string);
}

运行时输出以下结果:

1
12
123
1234
CHINA

  程序中用 PR 代表 printf。以 NL 代表执行一次“换行”操作。以 D 代表输出一个整型数据的格式符。以 D1 代表输出完1个整数后换行,D2 代表输出2个整数后换行,D3 代表输出3个整数后换行,D4 代表输出4个整数后换行。以 S 代表输出一个字符串的格式符。可以看到,程序中写输出语句就比较简单了,只要根据需要选择已定义的输出格式即可,连 printf 都可以简写为PR。

  可以参照例3,写出各种输入输出的格式(例如实型、长整型、十六进制整数、八进制整数、字符型等),把它们单独编成一个文件,它相当一个“格式库”,用 #include 命令把它“包括”到自己所编的程序中,用户就可以根据情况各取所需了。显然在写大程序时,这样做是很方便的。


版权声明:此文章为厂商在线—软件直销网(www.soft568.com)原创,如需转载请保留此链接,并勿随意改动文章内容!





不带参数的宏定义

http://www.doc88.com/p-992520052502.html


不带参数的宏定义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑控制android手机神器,scrcpy

我同事有一个华为手机,手机里内置了NFC,然后把手机靠近下电脑,当然的,一定需要是华为笔记本,然后电脑就弹出一个界面,控制手机的界面,这个功能非常好,加上NFC的功能,体验…

Modbus协议概念最详细介绍

★一文认识Modbus协议;”1 什么是Modbus?2 主从模式3 协议的分类3.1 Modbus ASCII3.2 Modbus RTU3.3 Modbus TCP3.4 Modbus Plus4 libmodbus5 freemodbus6 总结1 什么是Modbus?Modbus是Modicon(施耐德)公司于1979年开发…

OpenvSwitch实现kubernetes依赖的底层网络

kubernetes网络模型设计的一个基础原则是:每个Pod都拥有一个独立的IP地址,而且假定所有Pod都在一个可以直接连通的、扁平的网络空间中(在GCE里面是现成的网络模型)。在kubernetes中,IP是以Pod为单位进行分配的。一个Po…

该不该放弃嵌入式,单片机这条路?

知乎提问:本人,一个毕业工作两年在深圳工作的菜鸟单片机工程师。说说这两年来自己的一些看法。个人语言表述能力较差,加之经验不足,望见谅。1.程序编写1.1对架构能力要求较高。比如做一个自动售卖咖啡机,在开始写之前要…

6月份Asp.net源码推荐

整理了一下51aspx上6月份的Asp.net源码,特推荐一些给大家免费下载- 阿江酷站统计系统ASP.Net修正版Hits:262 2007-6-29阿江酷站单用户访问统计系统ASP.Net修正版 通过在要统计的页面潜入js脚本就可以对访问进行详细统计,可以按小时、日、月、年进行柱状图…

在家工作多年再回深圳找工作,会不会丢脸?

jiy发哥好。职场难题,江湖救急,请教你: 我刚毕业曾在深圳待过,那时候真是菜得什么都不会,折腾来折腾去一直没找到合适的坑,所以混了一两年又回老家小城市了。 小城市待遇普通,工作机会不多&…

数独

数独 时间限制:1000 ms | 内存限制:65535 KB难度:4描述数独是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据99盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个3*3宫内的数字均含1-9&…

Linux内存寻址方式

为什么要内存管理:早期的程序都是直接运行在物理地址上,也就是说这个程序所需要的空间不超过该机器的物理内存就不会有问题,但实际场景中都是多任务,多进程的,这种物理地址reserved给各个进程是不靠谱的。举个栗子&…

DotNetNuke 框架总揽

我总觉的学一样东东,最好是先在总体上对他有一个认识,而后才能更好的学习,最近刚好有机会学习DotNetNuke这个东西,就把所学到的都写下来,与大家一起分享,有不对的地方,大家一起讨论,…

超全!嵌入式必懂的CAN总线一文讲通了

嵌入式的工程师一般都知道CAN总线广泛应用到汽车中,其实船舰电子设备通信也广泛使用CAN,随着国家对海防的越来越重视,对CAN的需求也会越来越大。这个暑假,通过参加苏州社会实践,去某船舶电气公司实习几周,也…

Gentle中的数据表实体类相关自定义属性的设置和获得

1.自定义 表名属性 TableNameAttribute2.自定义 主键属性 PrimaryKeyAttribute3.自定义 列名属性 TableColumnAttribute 4.数据表person对应的实体类person.cs5.获得person.cs类型实体 对应的表名及字段名 下面的属性代码文件 都直接建立在App_Code下 以方便使用 1.自定义 表名…

WebSocket介绍

WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。 本文将使用Python编…

干,认识Audio框架还因此发现一个雷

我们最近出了一个问题,我们点击播放音乐,然后再点击停止播放的时候,喇叭还会输出一段杂音后喇叭才会停止输出。经过排查发现,在代码里面就做了这个功能代码在AudioFlinger.h 里frameworks/av/services/audioflinger/AudioFlinger.…

画图板-- 中点算法画圆

为了能以任意点为圆心画圆,我们可以把圆心先设为视点(相当于于将其平移到坐标原点),然后通过中点法扫描转换后,再恢复原来的视点(相当于将圆心平移回原来的位置)。圆心位于原点的圆有四条对称轴…

Linux 内核如何描述一个进程?

哈喽,我是吴同学,继续记录我的学习心得。一、关于写文章许多知识,书上或者网络上都有,就算这两个地方都没有,代码里也会有答案。但有时恰恰是 资料太多,反而让人难以检索出有用的信息。面对同样的资料&…

供应商关系管理SRM为企业的节流增贡献

在供应链下游的需求链上,企业为了增加市场份额、提高销售收入,更为重视面向客户的管理和信息化管理方面的投入,纷纷引入客户关系管理CRM的管理理念和信息化系统,在“客户第一”的经营策略下借助IT技术的手段来提高对客户的服务水平…

UART/I2C/SPI/1-wire四大通信接口的神解释

1、 裘千丈轻功水上漂之UART射雕英雄传中的裘千丈说,UART就是我的轻功水上漂过河。想从河上过(通信),提前布暗桩,行走时步伐按桩距固定(波特率提前确定),步幅太大或太小都会落水。为…

last_kmsg和ram console

相关文章Android 7.1使用脚本保存LOGCAT和KMSG日志到文件首先,在kernel里面通过printk吐log的时候会是下面的一个过程:printk会将信息格式化到kernel log buffer里面去。然后将这些格式化信息送到console去,在我们的系统里面有两个console&am…

字符串类

1.String类 常用方法: 参考:http://wenku.baidu.com/link?urltz-3Dpwj-JSJQdG6vSo0J1L1G9oJS4eQJjYgogieIzgjdNNLmj-U9EpWhOnVthz4egAKv0SNmLkqzNz2WsiZ2EmPGMu2UXhB6yy-E4yvMQ3 NB: 这里的”s1s2“是地址相等,而是s1.equals(s2)是内容相等…

github网页

GitHub主页创建仓库想必大家都有自己的Github账号吧,没有的可以到GitHub官网注册账号,注册完后,我们来下一步,在我们的GitHub上面右上角的New repository来创建一个仓库。 仓库名必须遵守相应格式:your_username.githu…