我在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
这是正确的还是我不必将参数提供给第一行?
答案 0 :(得分:6)
您想要编码
#ifndef SWAP
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
#ifndef
只是查看预处理器的符号表(是否存在某些给定的预处理器符号)。它并不关心SWAP
宏的参数。
当然在正版C ++ 11中,你应该更喜欢标准的std::swap
函数。它是类型安全的,更具可读性和更安全的(想想SWAP(t[i++],i)
是一个行为不端的例子)。
答案 1 :(得分:4)
在提出问题之前,您可以查看标准,以了解它是如何使用的。
来自标准 - §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预处理指令。 每个后续 类似函数的宏名称的实例后跟
(
作为下一个 预处理令牌引入了预处理令牌的序列 由定义中的替换列表替换(an 调用宏)。 ...
最后一节只是让你知道应该用ifndef
代替标识符写什么。从突出显示的部分可以清楚地看出。
在C
中,您需要做大量工作才能编写通用且正确的交换宏。对于两个相同的值,您的swap
将无法正常工作。
答案 2 :(得分:3)
简要回答你的问题,正如其他答案已经说明的那样。 #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
版本可提升类型安全性。
答案 3 :(得分:2)
这是不正确的。
#ifndef
的正确用法是:
#ifndef SWAP
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
来自http://en.cppreference.com/w/cpp/preprocessor/conditional:
#ifndef
identifier
答案 4 :(得分:0)
#ifndef SWAP
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
#endif
以上更改适用于c,c ++ 11。可能它适用于所有版本的c ++ XX。
答案 5 :(得分:0)
在更安全的智能指针之前的C ++旧时代std::shared_ptr<T>
&amp; std::unique_ptr<T>
使用new & delete
&amp; new [] & delete []
这些是我曾经在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
现在上面的宏是简单的,并且它不会阻止指针,表达式,引用等无效类型,但它应该适用于任何内置的默认基本类型。