C语言基础--宏函数

本文深入探讨C语言中的宏函数,对比宏函数与普通函数的优劣,讲解宏函数的定义、使用及其可能带来的副作用,同时提供避免副作用的策略。文章通过实例展示了宏函数在代码简化和运行效率提升方面的作用。

C语言基础–宏函数

1. 函数和数据类型

  1. 函数式宏(宏函数)和函数类比会更加灵活,我们通过两个例子来看一下。

    函数

    #include <stdio.h>
    int sqr_int(int x)
    {
        return x*x;
    }
    
    double sqr_double(double x)
    {
        return x*x;
    }
    
    int main(int argc , char *argv[])
    {
        int n;
        double x;
        
        printf("请输入一个整数:\n");
        scanf("%d",&n);
        printf("%d的平方是:%d\n",n,sqr_int(n));
        
        printf("请输入一个实数:\n");
        scanf("%lf",&n);
        printf("%lf的平方是:%lf\n",n,sqr_double(n));
        
        return 0;
    }
    
    • 当然了,如果计算long int,或者long long int型的数据类型的话我们还得编写该类型的函数。可以 遇见,如果这样下去代码中将会充斥着大量功能相近,名称相似的函数,看起来非常糟糕。
    • 下面来看一下函数式宏的实现:

    函数式宏

    #include <stdio.h>
    
    #define sqr(x) ((x)*(x))
    
    int main(int argc,char *argv[])
    {
        int n;
        double x;
        
        printf("请输入一个整数:\n");
        scanf("%d",&n);
        printf("%d的平方是:%d\n",n,sqr(n));
        
        printf("请输入一个实数:\n");
        scanf("%lf",&n);
        printf("%lf的平方是:%lf\n",n,sqr(n));
        
        return 0;
    }
    
    • 可以看到一个简单的宏函数就实现了几个函数才能实现的事情,当然了宏函数可不止这点儿能耐。
    • 本例中类似sqr(@) —>展开为((@)*(@))的形式就称为宏展开
    • 当调用该宏函数的时候,就会在调用位置将该宏函数展开为上述形式。
    • 值得注意的是宏定义只做替换不做计算,这一点很重要,后面会再提到。

2. 函数和函数式宏

  • 从上面可以看到函数式宏在某些时候可以替代函数的作用,它们的区别如下:
  1. 函数式宏是在编译时展开并填入程序的

  2. 而函数定义则需要为每个形参都定义各自的数据类型,返回值类型也只能为一种。函数更为严格。

  3. 函数默默的为我们进行一些复杂的操作,比如:

    1. 参数传递(将实参值赋值给形参)
    2. 函数调用和函数返回操作
    3. 返回值的传递

    而函数式宏只是做宏展开,并不做上述处理。

  4. 函数式宏能是程序的运行速度稍微提高一点儿,但是当函数中有大量的宏替换的时候,又会使得程序变得臃肿。(原理是宏函数只做替换,并没有类似函数调用的跳转、参数出入栈等操作,自然会提高函数的运行速度。这种运行速度加快体验在大量使用的时候尤为明显)

  5. 宏在使用的时候必须小心谨慎,避免出现问题。这一点是有宏的本身特性决定,即只做替换不做计算。举例来说明:

    宏的副作用

    情况一:

    #define sqr(a) ((a)*(a))
    

    若调用该函数式宏计算sqr(a++),展开后就变为:((a++) * (a++)),可以发现a执行了两次自增操作。这就会造成隐形的错误,比如我只是想将a自增1后在求其平方,但是结果却并非我们所想。

    情况二:

    宏定义与宏函数

    假如我们在sqr 和’('之间多敲了一个空格,如下

    #define sqr (a) ((a)*(a))
    

    那么此时函数式宏就变成了,宏定义了,也成对象式宏。即sqr会被编译器替换成(x) (x)*(x)

    在定义宏函数的时候注意宏函数名和’('之间不能有空格。

    情况三:

    #define sum(x,y) x + y	//注意:不规范的函数式宏的定义
    
    //调用
    z = sum(a,b) * sum(c,d);
    //编译器将其展开后就变为:
    z = a + b * c + d;	//这样是不是偏离了我们的本意
    

    因此,我们在定义函数式宏的时候与一定要每个参数以及整个表达式都用()括起来,就不会出错了。上面的就可以改为

    #define sum(x,y) ((x) + (y))	//正确的定义方法
    //调用
    z = sum(a,b) * sum(c,d);
    /编译器将其展开后就变为:
    z = ((a) + (b)) * ((c) + (d));
    

    总结,在定义和使用函数式宏的时候要注意避免其产生副作用

3. 不带参数的函数式宏

  • 函数式宏也可以像函数那样不带参数

    #define my_print() (printf("你好啊!\n"))
    

4. 函数式宏和逗号表达式

  • 下面将说明函数式宏使用时的一个重要技巧
#include <stdio.h>

#define puts_alert(str)  {putchar('\a');puts(str);}

int main(int argc,char *argv[])
{
    int n;
    
    printf("请输入一个整数:");
    scanf("%d",&n);
    
    if(n)
        puts_alert("这个数不是0.");
    else
        puts_alert("这个数是0.");
    
    return 0;
}
  • 当我们运行这个程序的时候会报错,无法运行。提示else 缺少if

    因为将该函数式宏展开后就变为:

#include <stdio.h>

#define puts_alert(str)  {putchar('\a');puts(str);}

//当然了,如果一行定义不下,有时候为了美观我们也这样写,'\'表示下一行还有内容
#define puts_alert(str)  {putchar('\a'); 	\
						   puts(str);}

int main(int argc,char *argv[])
{
    int n;
    
    printf("请输入一个整数:");
    scanf("%d",&n);
    
    if(n)
        {putchar('\a');puts(str);};
    else
        {putchar('\a');puts(str);};
    
    return 0;
}
  • 很明显,if下的复合语句’}‘后的’;‘会被认为是空语句,那么此时else再去找它上面的那个if的时候就找不到了,因此就会报错。当然了函数式宏中的’{}'也不能少,若少了又会报其他的错误。怎么解决呢?可以思考一下。当然了,也可以参见下面一节

5. 函数式宏和逗号表达式

  • 上面一节我们看到了当函数式宏中有多条需要执行的语句的时候我么遇到了麻烦,下面各处解决方法:
#include <stdio.h>

#define puts_alert(str)  (putchar('\a'),puts(str))

int main(int argc,char *argv[])
{
    int n;
    
    printf("请输入一个整数:");
    scanf("%d",&n);
    
    if(n)
        puts_alert("这个数不是0.");
    else
        puts_alert("这个数是0.");
    
    return 0;
}

//在if处展开
    if(n)
        (putchar('\a'),puts(str));
    else
        (putchar('\a'),puts(str));
  • 这里有个知识点,逗号表达式。逗号运算符:

    表达式a,表达式b —> 按顺序判断a和b,整个表达式最终生成表达式b的判断结果。当然了,若有多个表达式(整个逗号表达式的值为最后一个表达式的值),可以以此类推。

    举个例子:

  int a = 3,b = 4,z = 0;
  z = ++a,++b;	
//执行时a的值会自增1,b的值也会自增1,最终将b自增后的值赋给变量z
//此时z的值为5,也就是最终整个逗号表达式的结果。

可以看到:整个逗号表达式中每个语句都会被计算到

如果想要了解更多的逗号表达式的知识可以参考下我的另一博文:
逗号运算符与逗号表达式

6. 总结

  • 函数式宏在使用时很方便,正确使用的话不仅可以使得我们的程序变得简洁,而且可以提高我们程序的运行速度。
  • 但是这种方式有时也很容易出错,所以我们在使用的时候一定要非常小心,避免出错。
独立储能的现货电能量与调频辅助服务市场出清协调机制(Matlab代码实现)内容概要:本文围绕“独立储能的现货电能量与调频辅助服务市场出清协调机制”展开,提出了一种基于Matlab代码实现的优化模型,旨在协调独立储能系统在电力现货市场与调频辅助服务市场中的联合出清问题。文中结合鲁棒优化、大M法和C&CG算法处理不确定性因素,构建了多市场耦合的双层或两阶段优化框架,实现了储能资源在能量市场和辅助服务市场间的最优分配。研究涵盖了市场出清机制设计、储能运行策略建模、不确定性建模及求解算法实现,并通过Matlab仿真验证了所提方法的有效性和经济性。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事电力市场、储能调度相关工作的工程技术人员。; 使用场景及目标:①用于研究独立储能在多电力市场环境下的协同优化运行机制;②支撑电力市场机制设计、储能参与市场的竞价策略分析及政策仿真;③为学术论文复现、课题研究和技术开发提供可运行的代码参考。; 阅读建议:建议读者结合文档中提供的Matlab代码与算法原理同步学习,重点关注模型构建逻辑、不确定性处理方式及C&CG算法的具体实现步骤,宜在掌握基础优化理论的前提下进行深入研读与仿真调试。
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值