如何将 #ifndef 与宏参数一起使用
How to use #ifndef with macro argument?
我在header.h中有一个简单的代码-
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
这个 header.h 包含在 code.c 文件中,但我的要求是- 我希望首先检查 SWAP,例如-
#ifndef SWAP(a, b)
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
这是正确的还是我不必为第一行提供参数?
你想编码
#ifndef SWAP
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
#ifndef
只是查看预处理器的符号表(是否存在某些给定的预处理器符号)。它不关心您的SWAP
宏的参数。
当然,在正版 C++11 中,您应该更喜欢标准的std::swap
功能。它是类型安全的,更具可读性且更安全(将SWAP(t[i++],i)
视为行为异常的示例)。
这是不正确的。
#ifndef
的正确用法是:
#ifndef SWAP
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
从 http://en.cppreference.com/w/cpp/preprocessor/conditional:
#ifndef
identifier
在提出问题之前,您可以研究一下标准以了解它的使用方式。
从标准 -§6.10.1p5
表单的预处理指令
# ifdef identifier new-line groupopt # ifndef identifier new-line groupopt
检查标识符当前是否定义为宏名称。它们的条件分别等同于 #if 定义的标识符和 #if 定义的标识符。
然后,如果您不确定宏名称是什么以及参数是什么等,请再次使用。
来自标准§6.10.3.p10
表单的预处理指令
# define identifier lparen identifier-listopt ) replacement-list new-line # define identifier lparen ... ) replacement-list new-line # define identifier lparen identifier-list , ... ) replacement-list new-line
定义一个带有参数的类似函数的宏,其用法类似 在语法上与函数调用。参数由 可选的标识符列表,其范围从其 标识符列表中的声明,直到换行符 终止 #define 预处理指令。每个后续 类似函数的宏名称的实例,后跟下一个
(
预处理令牌引入了预处理令牌的顺序 由定义中的替换列表( 宏的调用)。...
最后一部分只是让您充分了解应该在ifndef
中写什么来代替标识符。从突出显示的部分可以清楚地看出。
C
您必须花费大量工作才能编写通用且正确的交换宏。对于两个相同的值,您的swap
将无法按预期工作。
简要回答您的问题,正如其他答案已经指出的那样。#ifdef
/#ifndef
条件只关心宏标识符,因此参数不是其语法的一部分。
但是,您的宏有几个应该解决的弱点。首先,请注意,使用 XOR 运算符进行交换,虽然是避免使用临时运算符的常用技巧,但如果两个参数是同一对象,则失败。这是因为第一个 XOR 的结果将为 0,而其余的 XOR 步骤无法恢复原始值。其次,此版本的宏对于指针类型失败,因为 XOR 需要整型类型。第三,宏多次调用参数,如果参数是具有副作用的表达式,则会导致问题。第四,宏中的复合语句应由do
保护。while (0)
允许宏扩展为语句。这使得宏在语法上更清晰,因此它后面的分号不是虚假的。
正如在另一个答案中所解释的,在C++中,使用std::swap
而不是定义自己的答案。遗憾的是,C 没有提供通用的交换实用程序函数。但是,编写泛型函数并不难:
static inline void swap_generic (void *a, void *b, void *t, size_t sz) {
if (a != b) {
memcpy(t, a, sz);
memcpy(a, b, sz);
memcpy(b, t, sz);
}
}
然后,您的宏可以调用此函数。
#ifndef SWAP
# ifdef __cplusplus
# define SWAP(a, b) std::swap(a, b)
# else
# define SWAP_ASSERT(X) _Static_assert(X, #X)
# if __STDC_VERSION__ < 201112L
# undef SWAP_ASSERT
# define SWAP_ASSERT(X) struct dummy
# endif
# define SWAP(a, b) do {
SWAP_ASSERT(sizeof(a) == sizeof(b));
char t[sizeof(a) != sizeof(b) ? -1 : sizeof(a)];
swap_generic(&(a), &(b), t, sizeof(a));
} while (0)
# endif
#endif
请注意,如果检测到C++,我们将如何使用std::swap
。
如果使用支持typeof
扩展的 C 编译器,则可以大大简化宏,因为不需要通用交换函数。
#ifndef SWAP
# ifdef __cplusplus
# define SWAP(a, b) std::swap(a, b)
# else
# define SWAP(a, b) do {
typeof(a) *p = &(a), *q = &(b), t = *p;
*p = *q;
*q = t;
} while (0)
# endif
#endif
请注意,typeof
版本可提高字体安全性。
#ifndef SWAP
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
上述更改适用于 C,C++11。 可能它适用于所有版本的 C++XX。
在C++的旧时代,在使用new & delete
和new [] & delete []
时,更安全的智能指针std::shared_ptr<T>
和std::unique_ptr<T>
出现之前,这些是我曾经在C++中使用的一些较旧的宏来帮助管理内存。
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p) { delete (p); (p) = nullptr; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if(p) { delete [] (p); (p) = nullptr; } }
#endif
类似于您要实现的目标:这些类型的宏的设计通常会查看tag - identifier
是否尚未定义。如果不是,则定义名称及其参数和功能,然后结束 if 定义。同样作为使用宏时的旁注,似乎做得有点过头了,但始终建议将每个参数名称括在括号中。因此,在您的情况下,您需要以下内容:
#ifndef SWAP
#define SWAP(a, b) if ( (a) != (b) ) {
((a) ^= (b));
((b) ^= (a));
((a) ^= (b));
} else {
((a) = (a));
((b) = (b));
}
#endif
现在上面的宏很简单,它不能阻止无效类型,如指针、表达式、引用等,但它应该适用于任何内置的默认基本类型。
- 如何将enable-if与模板参数和参数包一起使用
- 如何使我的 sizeof sum 结构与空参数包一起工作
- 概念可以与模板模板参数一起使用吗?
- 为什么当我尝试将priority_queue与参数一起使用作为指向结构的指针时会弹出错误
- 标准::原子::compare_exchange与两个memory_order参数一起使用的真实示例
- 如何在 C++ 中将 typedef 与类初始值设定项参数一起使用?
- 我可以将"token pasting operator"与"const"模板参数一起使用吗?
- 哪个强制转换应与模板类参数一起使用,dynamic_cast或reinterpet_cast?
- 将 boost::function 与指向派生类的共享指针的参数一起使用
- 如何将 #ifndef 与宏参数一起使用
- 如何将初始值设定项与右值引用参数一起使用 // 为什么不能使用另一个 C 样式数组变量初始化 C 样式数组
- 将模板类与引用非类型模板参数一起使用时出现链接器错误
- 如何将QDebug()与QT中的两个参数一起使用
- 将函数与指针参数一起使用作为另一个函数的参数
- 是否可以在C++中将宏条件与模板参数一起使用
- 提升program_options解析选项和参数一起
- 在C++中,如何仅将长选项与必需参数一起使用
- 当通过引用指针将函数与参数一起使用时会发生什么
- 将typedef与构造函数参数一起使用
- 将函数指针上的参数与默认值参数一起丢弃是否有效