【转载】#、##操作符,__VA_ARGS__

本文详细介绍了C语言预处理指令的使用方法,包括如何利用#将宏参数转换为字符串、如何使用##运算符连接两个参数、如何定义可变宏以及预定义宏__FILE__、__LINE__和__FUNCTION__的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.#——字符串
假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing).
#incldue <stdio.h>
#define PSQR(x) printf("the square of" #x "is %d.\n",(x)*(x))
int main(void)
{
    int y =4;
    PSQR(y);
    PSQR(2+4);
    return 0;
}
输出结果:
the square of y is 16.
the square of 2+4 is 36.
第一次调用宏时使用“y”代替#x;第二次调用时用“2+4"代#x。
 

2.##——连接两个参数
##运算符可以使用类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如:
#define XNAME(n) x##n
这样宏调用:
XNAME(4)
展开后:
x4
程序:
#include <stdio.h>
#define XNAME(x) x##4
#define PXN(n) printf("x"#n" = %d\n",x##n)
int main(void)
{
    int XNAME(1)=12;//int x1=12;
    PXN(1);//printf("x1 = %d\n", x1);
    return 0;
}
 
 
 
 
#include <iostream>
using namespace std;

#define TEST(pid) (cout<<para##pid<<endl);
#define TEST2(p) (cout<<#p<<endl);
int main()
{
    int para3 = 3;
    int para2 = 2;
    TEST(2);    //<==>cout<<para2<<endl;
    TEST(3);    //<==>cout<<para3<<endl;

    TEST2(test)        //<==>cout<<"test"<<endl;
    TEST2("test2");    //<==>cout<<""test2""<<endl;
    system("pause");
    return 0;
}


3.可变宏 ...和_ _VA_ARGS_ _
实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏_ _VA_ARGS_ _就可以被用在替换部分中,以表示省略号代表什么。比如:
#define PR(...) printf(_ _VA_ARGS_ _)
PR("hello");-->printf("hello");
PR("weight = %d, shipping = $.2f",wt,sp);
    -->printf("weight = %d, shipping = $.2f",wt,sp);
省略号只能代替最后面的宏参数。
 
4、__FILE__ 宏在预编译时会替换成当前的源文件名 

5、__LINE__宏在预编译时会替换成当前的行号  

6、 __FUNCTION__宏在预编译时会替换成当前的函数名称


转载地址:http://blog.chinaunix.net/space.php?uid=24170562&do=blog&id=2624783

### 宏参数 `__VA_ARGS__` 的使用方法 在 C 和 C++ 中,宏可以通过预处理器指令来扩展代码的功能。其中,`__VA_ARGS__` 是一种特殊的语法形式,用于支持 **可变参数宏**(variadic macros)。这种机制允许开发者创建能够接受任意数量参数的宏。 #### 基本概念 `__VA_ARGS__` 表示传递给宏的所有额外参数,在宏展开时会被替换为实际传入的内容[^1]。它通常与其他操作符一起使用,比如逗号连接器 (`##`) 或其他辅助工具。 以下是关于如何正确使用 `__VA_ARGS__` 的一些核心要点: --- ### 可变参数宏的基础结构 一个典型的可变参数宏定义如下所示: ```c #define MACRO_NAME(arg1, arg2, ...) \ some_code(arg1, arg2, __VA_ARGS__) ``` 在这个例子中: - 参数 `arg1` 和 `arg2` 是固定参数; - 使用省略号 `...` 来表示后续可能存在的零个或多个附加参数; - `__VA_ARGS__` 被用来代表这些附加参数的实际值。 当调用此宏时,可以像函数一样提供不同数量的参数。例如: ```c MACRO_NAME(a, b); MACRO_NAME(a, b, c, d, e); ``` 上述两种情况都会被正确解析并处理。 --- ### 实际案例分析 #### 示例 1:简单的日志记录宏 下面是一个常见的场景——构建一个带有动态消息的日志打印宏。 ```c #include <stdio.h> // 日志记录宏 #define LOG(level, fmt, ...) printf("[%s] " fmt "\n", level, __VA_ARGS__) int main() { LOG("INFO", "This is an informational message."); LOG("ERROR", "An error occurred with code %d.", 404); return 0; } ``` 运行结果将是: ``` [INFO] This is an informational message. [ERROR] An error occurred with code 404. ``` 这里的关键在于通过 `__VA_ARGS__` 将格式化字符串及其对应的变量自动插入到最终生成的代码片段中[^2]。 --- #### 示例 2:条件判断中的应用 另一个典型用途是在某些条件下执行特定逻辑的同时保留灵活性。例如 TensorFlow 提供了一个名为 `OP_REQUIRES_OK` 的宏[^3],其内部利用了类似的技巧: ```cpp #define OP_REQUIRES_OK(CTX, STATUS) \ do { \ ::tensorflow::Status _status = (STATUS); \ if (!_status.ok()) { \ CTX->CtxFailureWithWarning(__FILE__, __LINE__, _status.message()); \ return; \ } \ } while(false) void ExampleFunction(OpKernelContext* ctx) { OP_REQUIRES_OK(ctx, SomeOperationThatReturnsAStatus()); } ``` 在此处可以看到,`__VA_ARGS__` 替代部分由用户提供自定义状态对象作为输入数据源之一。 --- ### 特殊注意事项 尽管 `__VA_ARGS__` 非常强大,但在实际开发过程中仍需注意几个常见陷阱: 1. 如果没有指定任何额外参数,则可能会导致编译失败或者行为异常。为了避免这种情况发生,可以在设计阶段加入保护措施,例如有条件地移除多余的分隔符 `,`。 解决方案之一就是借助于双井号运算符(`##`)消除不必要的逗号: ```c #define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) ``` 2. 不同平台之间可能存在细微差异;因此建议始终查阅目标环境文档确认兼容性问题。 --- ### 总结 综上所述,掌握好 `__VA_ARGS__` 的基本原理以及灵活运用方式对于编写高效简洁且易于维护的程序至关重要。无论是简单调试还是复杂框架集成都离不开这项技术的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值