简体   繁体   English

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

[英]How to use #ifndef with macro argument?

I have a simple code in header.h- 我在header.h中有一个简单的代码 -

#define SWAP(a, b)  {a ^= b; b ^= a; a ^= b;} 

This header.h is included in a code.c file, but my requirement is- I want SWAP to be checked first like- 这个header.h包含在code.c文件中,但我的要求是 - 我希望首先检查SWAP,如下所示 -

#ifndef SWAP(a, b)
#define SWAP(a, b)  {a ^= b; b ^= a; a ^= b;}
#endif

Is this correct or I don't have to provide the argument to the first line? 这是正确的还是我不必将参数提供给第一行?

You want to code 你想要编码

#ifndef SWAP
#define SWAP(a, b)  {a ^= b; b ^= a; a ^= b;}
#endif

The #ifndef is just looking into the preprocessor's symbol table (for the presence or absence of some given preprocessor symbol). #ifndef只是查看预处理器的符号表(是否存在某些给定的预处理器符号)。 It does not care about the arguments of your SWAP macro. 它不关心您的SWAP宏的参数。

Of course in genuine C++11 you should prefer the standard std::swap function. 当然在正版C ++ 11中你应该更喜欢标准的std::swap函数。 It is typesafe, more readable, and safer (think about SWAP(t[i++],i) as a misbehaving example). 它是类型安全的,更具可读性和更安全的(想想SWAP(t[i++],i)作为一个行为不端的例子)。

Before asking the question you could have looked into the standard to get an idea of how it is used. 在提出问题之前,您可以查看标准,以了解它是如何使用的。

From standard - §6.10.1p5 从标准 - §6.10.1p5

Preprocessing directives of the forms 表单的预处理指令

 # ifdef identifier new-line groupopt # ifndef identifier new-line groupopt 

check whether the identifier is or is not currently defined as a macro name . 检查标识符当前是否定义为宏名称 Their conditions are equivalent to #if defined identifier and #if !defined identifier respectively. 它们的条件分别等同于#if定义的标识符和#if!定义的标识符。

Then again if you are not sure what is the macro name and what are the parameters etc.. 如果您不确定宏名称是什么以及参数是什么等,那么又一次。

From standard §6.10.3.p10 从标准§6.10.3.p10开始

A preprocessing directive of the form 表单的预处理指令

 # 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 

defines a function-like macro with parameters , whose use is similar syntactically to a function call. 定义了一个带有参数的类函数宏 ,它的用法在语法上类似于函数调用。 The parameters are specified by the optional list of identifiers, whose scope extends from their declaration in the identifier list until the new-line character that terminates the #define preprocessing directive. 参数由可选的标识符列表指定,其范围从它们在标识符列表中的声明扩展到终止#define预处理指令的换行符。 Each subsequent instance of the function-like macro name followed by a ( as the next preprocessing token introduces the sequence of preprocessing tokens that is replaced by the replacement list in the definition (an invocation of the macro). ... 类似函数的宏名称的每个后续实例后跟一个(作为下一个预处理标记引入了预定处理标记的序列,该标记由定义中的替换列表(宏的调用)替换。)

The last section just will let you know enough what should be written in the ifndef in place of identifier. 最后一节只是让你知道应该在ifndef中写下什么代替标识符。 It is clear from the highlighted parts. 从突出显示的部分可以清楚地看出。

In C you have to a considerable amount of work to write a generic and correct swap macro. C您需要花费大量的工作来编写通用且正确的交换宏。 For two same value your swap will not work as you expect it to. 对于两个相同的值,您的swap将无法按预期工作。

To briefly answer your question, it is as the other answers have already stated. 简要回答你的问题,正如其他答案已经说明的那样。 The #ifdef / #ifndef conditional only cares about the macro identifier, so the arguments are not part of its syntax. #ifdef / #ifndef条件只关心宏标识符,因此参数不是其语法的一部分。

However, your macro has a couple of weaknesses that should be addressed. 但是,您的宏有一些应该解决的弱点。 First, note that using the XOR-operator for swapping, although a commonly taught trick to avoid using a temporary, fails if the two arguments are the same object. 首先,请注意,如果两个参数是同一个对象,使用XOR运算符进行交换虽然是一种常用的避免使用临时的技巧,但会失败。 This is because the result of the first XOR would be 0, and the remaining XOR steps cannot recover the original value. 这是因为第一个XOR的结果为0,剩余的XOR步骤无法恢复原始值。 Second, this version of the macro fails for pointer types, because XOR wants integral types. 其次,这个版本的宏因指针类型而失败,因为XOR需要整数类型。 Third, the macro invokes the arguments multiple times, which will cause problems if the argument is an expression with side effects. 第三,宏多次调用参数,如果参数是带副作用的表达式,将导致问题。 Fourth, compound statements in a macro should be guarded by do .. while (0) to allow the macro to expand into a statement. 第四,宏中的复合语句应该通过do ... while (0)来保护,以允许宏扩展为语句。 This makes the macro syntactically cleaner, so that a semi-colon after it is not spurious. 这使得宏在语法上更清晰,因此在它之后的分号不是虚假的。

As explained in a separate answer, in C++, use std::swap instead of defining your own. 正如在单独的答案中解释的那样,在C ++中,使用std::swap而不是定义自己的。 Unfortunately, C does not provide a generic swapping utility function. 不幸的是,C没有提供通用的交换实用程序功能。 However, it is not difficult to author a generic function: 但是,编写通用函数并不困难:

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);
    }
}

Your macro could then invoke this function. 然后,您的宏可以调用此函数。

#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

Note how we use std::swap if C++ is detected. 注意如果检测到C ++,我们如何使用std::swap

If you use a C compiler that supports the typeof extension, then the macro can be simplified greatly, since you do not need a generic swapping function. 如果您使用的C编译器支持typeof扩展,那么宏可以大大简化,因为你并不需要一个通用的交换功能。

#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

Note that the typeof version promotes better type safety. 请注意, typeof版本可以提高类型安全性。

It is incorrect. 这是不正确的。

The correct usage of #ifndef is: #ifndef的正确用法是:

#ifndef SWAP
#define SWAP(a, b)  {a ^= b; b ^= a; a ^= b;}
#endif

From http://en.cppreference.com/w/cpp/preprocessor/conditional : 来自http://en.cppreference.com/w/cpp/preprocessor/conditional

#ifndef identifier #ifndef identifier

#ifndef SWAP
#define SWAP(a, b)  {a ^= b; b ^= a; a ^= b;}
#endif

above change will work for c,c++11. 以上更改适用于c,c ++ 11。 probably it will work for all the versions of c++XX. 可能它适用于所有版本的c ++ XX。

In the older days of C++ before safer smart pointers std::shared_ptr<T> & std::unique_ptr<T> came around when using new & delete & new [] & delete [] these are some older macros that I once used in C++ for helping to manage memory. 在C ++的旧时代,更安全的智能指针std::shared_ptr<T>std::unique_ptr<T>在使用new & deletenew [] & 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 

Similar to what you are trying to achieve: the design of these types of macros typically look to see if the tag - identifier is not yet defined. 与您尝试实现的类似:这些类型的宏的设计通常会查看tag - identifier是否尚未定义。 If it isn't then you define the name along with its parameter and functionality, then you end the if definition. 如果不是,则定义名称及其参数和功能,然后结束if定义。 Also as a side note when working with macros it may seem like over doing it but it is always advisable to enclose each parameter name in parenthesis. 另外作为使用宏时的旁注,看起来似乎过度了,但始终建议将每个参数名称括在括号中。 So in your case you would want something like: 所以在你的情况下你会想要这样的东西:

#ifndef SWAP
    #define SWAP(a, b)  if ( (a) != (b) ) { \
                            ((a) ^= (b));   \
                            ((b) ^= (a));   \
                            ((a) ^= (b));   \
                        } else {            \ 
                            ((a) = (a));    \
                            ((b) = (b));    \
                        }                   
#endif

Now the above macro is simplistic and it doesn't prevent from invalid types such as pointers, expressions, references etc., but it should work with any built in default basic types. 现在上面的宏是简单的,它不会阻止指针,表达式,引用等无效类型,但它应该适用于任何内置的默认基本类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM