如何将 #ifndef 与宏参数一起使用

How to use #ifndef with macro argument?

本文关键字:参数 一起 #ifndef      更新时间:2023-10-16

我在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:

#ifndefidentifier

在提出问题之前,您可以研究一下标准以了解它的使用方式。

从标准 -§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 & deletenew [] & 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

现在上面的宏很简单,它不能阻止无效类型,如指针、表达式、引用等,但它应该适用于任何内置的默认基本类型。