C语言中带返回值的宏定义方式

目录
  • C语言中带返回值的宏定义
    • 宏定义编写
    • 宏定义分析
    • 宏定义验证
    • 经验总结
  • C语言中一些宏定义和常用的函数
    • typeof 关键字
    • snprintf()函数的作用
    • __builtin_expect的作用
    • C语言中常用的预定义
    • 反斜杠的作用
  • 总结

C语言中带返回值的宏定义

相信大家在实际工作中,一定有遇到需要编写一个宏定义,且希望它能带返回值的场景吧?

比如我之前就遇到一个场景,早期的代码是使用函数实现的功能,现在想换成宏定义,但是又要保留之前调用函数的代码不动,这样我就只能想办法写一个带返回值的宏了。

宏定义编写

直接上demo:

#include <stdio.h>

/* always return 1 */
#define RETURN_MACRO()            ({do {} while(0);1;})
#define RETURN_MACRO2()            1

/* return a+b */
#define A_PLUS_B_MACRO(a, b)    ({int ret; ret = (a) + (b); ret;})
#define A_PLUS_B_MACRO2(a, b)    ({int ret; ret = add((a), (b)); ret;})

int add(int a, int b)
{
    return (a + b);
}

int main(int argc, const char *argv[])
{
    int a = 6;
    int b = 7;

    printf("Hello world !\n");
    printf("RETURN_MACRO: %d\n", RETURN_MACRO());
    printf("RETURN_MACRO2: %d\n", RETURN_MACRO2());
    printf("a + b = %d\n", A_PLUS_B_MACRO(a, b));
    printf("a + b = %d\n", A_PLUS_B_MACRO2(a, b));

    return 0;
}

宏定义分析

为了分析宏定义的写法,我们得知道宏定义最终被展开是什么样的。

我在之前的博文中有提到,使用gcc编译器的话,可以在CFLAGS上加上-save-temps=obj这个编译选项,这样就可以得到预编译处理之后的文件,后缀名是.i。

我们使用编译脚本编译之后,得到.i文件如下:

//前面的内容忽略

# 3 "main.c" 2
# 12 "main.c"

# 12 "main.c"
int add(int a, int b)
{
 return (a + b);
}

int main(int argc, const char *argv[])
{
 int a = 6;
 int b = 7;

 printf("Hello world !\n");
 printf("RETURN_MACRO: %d\n", ({do {} while(0);1;}));
 printf("RETURN_MACRO2: %d\n", 1);
 printf("a + b = %d\n", ({int ret; ret = (a) + (b); ret;}));
 printf("a + b = %d\n", ({int ret; ret = add((a), (b)); ret;}));

 return 0;
}

从.i文件我们可以看到,宏定义被正常展开,下面确认下功能是否正常。

宏定义验证

我们执行编译出来的可执行文件:

return_macro$ ./test 
Hello world !
RETURN_MACRO: 1
RETURN_MACRO2: 1
a + b = 13
a + b = 13

验证成功。

经验总结

  • 在C语言里面,可以使用({aaa; bbb; ccc;})来实现宏定义带返回值;这里的返回值是最后一个;的值。
  • 注意里面的()和{}都不能少,否则可能会破坏代码的语法结构,导致得不到正确的答案。

C语言中一些宏定义和常用的函数

typeof 关键字

如果你是 C++ 程序员,应该接触过 C++11 里的 decltype 操作符,它的作用是自动推导表达式的数据类型,以解决泛型编程中有些类型由模板参数决定而难以(甚至不可能)表示的问题。

其实这个特性在 C 语言中也早有类似的实现,GNU C 标准中的一个扩展特性 typeof 作用与 decltype 类似。

__typeof__ (ret) errnum = (ret); 

snprintf()函数的作用

#include<stdio.h>
int snprintf(char* dest_str,size_t size,const char* format,...);

【函数功能】:

先将可变参数 “…” 按照format的格式格式化为字符串,然后再将其拷贝至dest_str中。

如果格式化后的字符串长度小于size,则将字符串全部拷贝至dest_str中,并在字符串结尾处加上‘\0’; 如果格式化后的字符串长度大于或等于size,则将字符串的(size-1)拷贝至dest_str中,然后在字符串结尾处加上’\0’. 函数返回值是 格式化字符串的长度。

__builtin_expect的作用

__builtin_expect(errnum != 0, 0)

这个指令是gcc引入的,作用是"允许程序员将最有可能执行的分支告诉编译器"。

这个指令的写法为:__builtin_expect(EXP, N)。意思是:EXP==N的概率很大。

一般的使用方法是将__builtin_expect指令封装为LIKELY和UNLIKELY宏。

C语言中常用的预定义

  • __LINE__:当前程序行的行号,表示为十进制整型常量
  • __FILE__:当前源文件名,表示字符串型常量
  • __DATE__:转换的日历日期,表示为Mmm dd yyyy 形式的字符串常量,Mmm是由asctime产生的。
  • __TIME__:转换的时间,表示"hh:mm:ss"形式的字符串型常量,是有asctime产生的。(asctime貌似是指的一个函数)
  • __STDC__:编辑器为ISO兼容实现时位十进制整型常量
  • __func__:它指示所在的函数
  • __assert_perror_fail:打印一条包含错误码ERRNUM的错误消息,并终止程序

反斜杠的作用

反斜杠起到换行作用,用于宏定义和字符串换行。其中宏定义中使用居多。

如果一行代码有很多元素,导致太长影响阅读,可以通过在结尾加\的方式,实现换行,编译时会忽略\及其后的换行符,当做一行处理。

#define CHECK_ACTION_RETURN(expr) \
    if (!expr) { \
        printf(":failed(%d)\n", ret); \
        return ret; \
                } else { \
        printf(":ok\n"); \
                }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C语言深入讲解宏的定义与使用方法

    目录 一.C语言中的宏定义 二.宏定义表达式 三.宏表达式与函数的对比 四.有趣的问题 五.强大的内置宏 六.小结 一.C语言中的宏定义 #define是预处理器处理的单元实体之一 #define 定义的宏可以出现在程序的任意位置 #define 定义之后的代码都可以使用这个宏 #define 定义的宏常量可以直接使用 #define 定义的宏常量本质为字面量 下面的宏常量定义正确吗? 编写代码来测试: #define ERROR -1 #define PATH1 "D:\test\test.c

  • C语言宏定义使用分析

    1.如何区分宏定义中的"宏名称"和"宏字符串"?对于带参数的宏又该注意什么? 在宏定义中,"宏名称"和"宏字符串"是通过"空格"来区分的.编译器在处理时宏定义时,首先从"#define"后第一个空格开始读取字符串,直到遇见下一个空格为止,两个空格之间的字符串为"宏名称",确定好"宏名称"之后,本行的所有其他字符串都为"宏字符串"

  • 详解C语言中的#define宏定义命令用法

    #define 命令#define定义了一个标识符及一个串.在源程序中每次遇到该标识符时,均以定义的串代换它.ANSI标准将标识符定义为宏名,将替换过程称为宏替换.命令的一般形式为: #define identifier string 注意: 1.该语句没有分号.在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束. 2.宏名定义后,即可成为其它宏名定义中的一部分. 3.宏替换仅仅是以文本串代替宏标识符,前提是宏标识符必须独立的识别出来,否则不进行替换.例如: #define XYZ t

  • C语言中带返回值的宏定义方式

    目录 C语言中带返回值的宏定义 宏定义编写 宏定义分析 宏定义验证 经验总结 C语言中一些宏定义和常用的函数 typeof 关键字 snprintf()函数的作用 __builtin_expect的作用 C语言中常用的预定义 反斜杠的作用 总结 C语言中带返回值的宏定义 相信大家在实际工作中,一定有遇到需要编写一个宏定义,且希望它能带返回值的场景吧? 比如我之前就遇到一个场景,早期的代码是使用函数实现的功能,现在想换成宏定义,但是又要保留之前调用函数的代码不动,这样我就只能想办法写一个带返回值的

  • Java 带参数与带返回值的方法的定义和调用

    目录 带参数方法的定义和调用 形参和实参 带参数方法练习 带返回值的方法的定义和调用 带返回值的方法定义 带返回值的方法调用 带参数方法的定义和调用 形参和实参 形参:方法定义中的参数 相当于变量定义格式,例int number 实参:方法调用中参数 等同于变量或常量,例如10   , number 带参数方法练习 需求: 设计一个方法用于打印两个数中最大数,数据来自于方法参数 思路: 1.定义一个方法,用于打印两个书中的最大数,例如getMax() public static void get

  • VS2019中scanf返回值被忽略的问题及其解决方法

    昨天在使用Visual Studio 2019编写C语言程序时遇到了scanf返回值被忽略问题 因为我也是刚开始学习C语言,第一次遇到这种问题,也不知道怎么回事,然后就上Chrome研究了一番,才知道原因,并且找到了几种分散在个个角落的解决方法,我在这里归纳总结一下. 问题原因: 在ANSI C中只有scanf(),没有scanf_s(),但是scanf()在读取时不检查边界,所以可能会造成内存泄漏.于是Microsoft公司在VS编译器中提供了scanf_s(),如果想继续使用scanf这个不

  • jquery+ajax请求且带返回值的代码

    现在比较流行使用jquery的ajax来实现一些无刷新请求效果,本章节提供一个非常简单的代码实例供大家参考之用,希望能够给需要的朋友带来一定的帮助,代码如下: <script type="text/javascript"> /* 请求Ajax 带返回值,并弹出提示框提醒 --------------------------------------------------*/ function getAjax(url,parm,callBack) { $.ajax({ typ

  • C语言之函数返回值与参数传递案例教程

    C语言函数返回值与参数传递 一:参数传递 C语言的函数中必不可少的就是参数传递,可以采用传值和传指针两种方式. 1.传值的形式:只是将参数值的拷贝传给函数,并非参数本体如: int test(int x,int y) { x=2*y; return x; } int main(void) { int a=0,b=5; printf("%d,%d\n\r",test(a,b),a); return 0; } 运行结果为: 10,0 即传值方式只可以将实参传递给函数,不可以改变实参本身.

  • Vue的export default和带返回值的data()及@符号的用法说明

    目录 export default和带返回值data()及@符号用法 export和export default的使用 export的使用 export default的使用 export default和带返回值data()及@符号用法 一直以来很费解为什么vue组件有的写成export default,有什么用? 声明一个vue,相当于 new Vue({}) 达到可复用的目的,也就是说,export default 相当于导出当前vue组件,在其它引入当前组件时可以使用当前组件中的方法和变

  • Java带返回值的方法的定义和调用详解

    目录 带返回值的方法练习 方法的注意事项 方法注意事项 方法通用格式 带返回值的方法练习 需求: 设计一个方法可以获取两个数的较大值,数据来自于参数 思路: 1. 定义一个方法,用于获取两个数中的较大数 public static int getMax(int a,int b){ } 2.使用分支语句分两种情况对两个数的大小进行处理 if (a>b) { }else{ } 3. 根据题设分别设置两种情况下对应返回值结果 if (a>b) { return a; }else{ return b;

  • shell函数内调用另一个函数(不带返回值和带返回值)

    目录 一.函数B调用不带返回值的函数A 二.函数B调用带返回值的函数A,并接收函数A的返回值进行输出 一.函数B调用不带返回值的函数A 新建文件,命名为 test.sh,添加如下代码: #!/bin/bash # 即将被调用的函数A function A(){ a="aaa" echo $a } # 函数B,直接调用A function B(){ A echo "bbb" } B 命令行中通过sh test.sh执行结果: 二.函数B调用带返回值的函数A,并接收函数

  • Mysql带返回值与不带返回值的2种存储过程写法

    过程1:带返回值: drop procedure if exists proc_addNum; create procedure proc_addNum (in x int,in y int,out sum int) BEGIN SET sum= x + y; end 然后,执行过程,out输出返回值: call proc_addNum(2,3,@sum); select @sum; 过程2:不带返回值: drop procedure if exists proc_addNum; create

  • 创建公共调用 jQuery Ajax 带返回值

    复制代码 代码如下: <script type="text/javascript"> /* 请求Ajax 带返回值,并弹出提示框提醒 --------------------------------------------------*/ function getAjax(url, parm, callBack) { $.ajax({ type: 'post', dataType: "text", url: url, data: parm, cache:

随机推荐