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

时间:2018-01-17 05:44:28

标签: c++ c c++11 macros

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

这是正确的还是我不必将参数提供给第一行?

6 个答案:

答案 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

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