前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言有参数宏定义与无参数宏定义

C语言有参数宏定义与无参数宏定义

作者头像
mathor
发布2018-06-22 10:36:22
2.8K0
发布2018-06-22 10:36:22
举报
文章被收录于专栏:mathor

前两天上课,被JAVA老师问懵了,老师问:“你们学C语言,有没有写过带参的宏玩一玩”,说实话,我根本没听过什么带参的宏,我只用过宏定义,所以我下来一定要找个时间把这“带参的宏搞懂”,于是就有了这篇文章。

       C语言中宏定义分两种,无参的宏有参的宏

1.无参数的宏

       无参数宏定义的一般形式为:

代码语言:javascript
复制
#define name value//name是你起的名字,就跟起函数名一样,value是你要给这个名字赋予什么值
//示例:
#include <iostream>
using namespace std;
#define pi 3.14
int main()
{
        int r = 2;
        double s = pi*r*r;
        cout<<s;
}

       这种宏定义要求编译预处理程序将源程序随后所有宏名(注释与字符串常量除外)均用值替换。无参数的宏没什么好说的,但还是有些地方使用时要注意。

几点注意:

1. 在宏定义的#之前可以有若干个空格、制表符,但不允许有其它字符。宏定义在源程序中单独另起一行,换行符是宏定义的结束标志(不能在末尾加分号)。如果一个宏定义太长,一行不 够时,可采用续行的方法。续行是在键人回车符之前先键入符号"/"。注意回车要紧接在符号"/"之后,中间不能插入其它符号。

       2. 宏定义的有效范围称为宏定义名的辖域(也可以叫做生命周期,类似于变量的生命周期),辖域从宏定义的定义结束处开始到其所在的源程序文件末尾。宏定义名的辖域不受分程序结构的影响。可以用预处理命令#undef终止宏定义名的辖域。

3. 在新的宏定义中,可以使用前面已定义的宏名,示例:

代码语言:javascript
复制
# define R 2.5
# define PI 3.1415926
# define Circle 2*PI*R
# define Area PI*R*R

       4. 如有必要,宏名可被重复定义。被重复定义后,宏名原先的意义被新意义所代替。

2.有参数的宏

       有参数宏的定义形式一般为:

代码语言:javascript
复制
#define name(参数1,参数2,....) sentence//sentencen表示语句
//示例:
#define max(a,b) (a)>(b)?(a):(b)
#include <iostream>
using namespace std;
int main()
{
        int x = max(1+3,1+4);
        cout<<"x = "<<x;//x = 5
        return 0;
}

       注意!注意!注意!我这里为什么a和b要加括号?我换个示例,如果不加括号,看输出什么

代码语言:javascript
复制
#define pw(x) x*x
#include <iostream>
using namespace std;
int main()
{
        int x = pw(3+4);
        cout<<x;
        return 0;
}

       在我给出答案之前,或者我提醒你之前,你估计想不打这会输出什么,你可能会认为会输出49,但答案是22。为什么是22不是49?哪里错了?哪里都没错,他只不过依据了正常的加减乘除顺序而已,因为你没加括号,所以他不会将3+4作为一个整体来进行乘法运算,而是这个样子3+4*3+4,先乘除后加减,你说这等于多少?所以在进行宏定义的时候,多加几个括号,总没问题。

       带参的宏,类似与函数,看下面的程序,输出我给了,读者可以先分析

代码语言:javascript
复制
#include <iostream>
using namespace std;
#define swap1(a,b) t=a;a=b;b=t;
int swap2(int c,int d)
{
        int t;
        t = c;
        c = d;
        d = t;
}
int main()
{
    int a,b,c,d,t;
    a = 5;
    b = 3;
    c = 5;
    d = 3;
    swap1(a,b);
        swap2(c,d);
        cout<<a<<" "<<b<<endl;//3 5
        cout<<c<<" "<<d;//5 3
    return 0;
}

       你会发现函数,并没有交换实参,而宏交换了,但是如果把函数中的参数改为指针或者引用就能成功交换了。下面给出带参的宏和函数的区别:

       1. 宏会在编译器在对源代码进行编译的时候进行简单替换,不会进行任何逻辑检测,即简单代码复制而已。        2. 宏进行定义时不会考虑参数的类型。        3. 参数宏的使用会使具有同一作用的代码块在目标文件中存在多个副本,即会增长目标文件的大小。        4. 参数宏的运行速度会比函数快,因为不需要参数压栈/出栈操作。        5. 函数只在目标文件中存在一处,比较节省程序空间。        6. 函数的调用会牵扯到参数的传递,压栈/出栈操作,速度相对较慢。        7. 函数的参数存在传值和传地址(指针)的问题,参数宏不存在。

       3. 宏中”#”和”##”的用法

       一般用法:

1.使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起,看下面的示例:

代码语言:javascript
复制
#include<cstdio>
#include<climits>
using namespace std;
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
int main()
{
        printf(STR(vck)"\n");           //输出字符串"vck"
        printf("%d", CONS(2,3)); // 2e3 输出:2000
        return 0;
}

2.当宏参数是另一个宏的时候,需要注意凡是宏定义里有用''#''或''##''的地方宏参数是不会再展开,看示例:

代码语言:javascript
复制
//1.非"#"和"##"的情况 
#include<cstdio> 
#include<climits> 
using namespace std;
#define TOW (2) 
#define MUL(a,b) (a*b)
int main() 
{ 
        printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
}

//2.当有''#''或''##''的时候  #include<cstdio> #include<climits> using namespace std; #define A (2) #define STR(s) #s #define CONS(a,b) int(a##e##b) int main() {         printf("int max: %s\n", INT_MAX);//INT_MAX         printf("%s\n", CONS(A, A));//compile error }

       这个时候,INT_MAX和A都不会在被展开,解决这个问题的方法很简单,多加一层转换宏,加这层宏的用意是把所有宏的参数在中间曾全部展开。看下面的示例

代码语言:javascript
复制
//2.当有"#"或"##"的时候 
#include <bits/stdc++.h>
using namespace std;
#define _STR(s) #s
#define STR(s) _STR(s) // 转换宏
int main()
{
        printf("int max: %s\n",STR(INT_MAX));//int max: 2147483647
        return 0;
}

感谢阅读

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档