多行预处理宏

104

如何创建多行预处理器宏?我知道如何创建一行:

#define sqr(X) (X*X)

但我需要像这样的东西:

#define someMacro(X)
    class X : public otherClass
    {
         int foo;
         void doFoo();
    };

如何让这个起作用?

这只是一个例子,真正的宏可能非常长。


你可以通过搜索 SO 轻松地找到答案。例如:http://stackoverflow.com/questions/4007865/few-questions-about-the-c-preprocessor - Forever Learner
这里有不同的方法:http://www.parashift.com/c++-faq/macros-with-multi-stmts.html - Ayrat
相关:https://dev59.com/xXVD5IYBdhLWcg3wE3Ro - Ciro Santilli OurBigBook.com
那个SQR宏是错误的:另请参阅https://stackoverflow.com/a/76719135/480982 - Thomas Weller
7个回答

162

你使用 \ 作为换行符的转义字符。

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

编辑: 如@abelenky在评论中指出,\字符必须是行尾最后一个字符。如果不是(即使后面只有空格),在其后的每一行都会产生令人困惑的错误消息。


55
注意事项:确保反斜杠(\)是该行的最后一个字符。在C语言中,空格通常不重要,但在这种情况下,行末看不见的空白字符可能会导致错误。 - abelenky
2
应该补充说明的是,生成的文本在一行上。因为C语言将标记之间的所有空格视为相同,所以通常并不太重要,但仍然需要注意。 - Peter - Reinstate Monica
我建议做的另一件事是在宏定义的所有有用行之后加上 \,然后添加注释,比如 // 宏定义后需要空白行。确保所有宏定义的行都以 \ 结尾有时比确保除最后一行外的所有行都这样做要容易些。 - supercat
2
我不知道你可以像那样使用按位异或来交换变量,但我希望我能想到它! - cmarangu

25

通过在每行末尾添加反斜杠 (\),您可以使宏跨越多行:

#define F(x) (x)   \
              *    \
             (x)

23
请注意,正如Kerrek SB和coaddict所指出的那样,应该在被接受的答案中指出, 始终在参数周围放置大括号。 sqr示例是计算机科学课程中教授的简单示例。

这里的问题是:如果您按照您的方式定义它,当您说“sqr(1 + 5)”时会发生什么? 您将获得“1 + 5 1 + 5”或11
如果您正确地将括号放在它周围,则#define sqr(x) ((x)
(x))
您将得到((1 + 5) * (1 + 5)),或者我们想要的36 ...太棒了。

Ed S.在此帖子后编辑了他的答案。

请注意- Ed S.在此帖子后编辑了他的答案。


sqr(++i)怎么样?(假设我们有一个int i):) - Géza Török
我这么做只是为了练习,显然 i 在被宏替换时被递增(在这种情况下它被替换了两次),然后被乘。因此,sqr(++5) == ((7) * (7)) - jiveturkey
4
@GézaTörök,将sqr(++i)展开为((++i)*(++i))会引起未定义行为,因为该语句中i的值被修改了多次(操作之间没有顺序点)。 - moooeeeep

7

虽然这不是原始问题的一部分,但其他答案都没有提到在多行宏中嵌入的注释需要特别注意。

  • C++风格的注释不能出现在任何具有行延续转义字符的行上。
  • C风格的注释不能跨越由行延续转义字符分隔的多行。

例子:

// WRONG:
#define someMacro(X)          \
// This comment is a problem. \
class X : public otherClass   \
{                             \
     int foo;                 \
     void doFoo();            \
};

// WRONG:
#define someMacro(X)        \
/* This comment is also     \
 * a problem. */            \
class X : public otherClass \
{                           \
     int foo;               \
     void doFoo();          \
};

// OK:
#define someMacro(X)                \
/* This comment is fine. */         \
class X : public otherClass         \
{                                   \
     int foo; /* This is OK too! */ \
     void doFoo();                  \
};

数字2有什么问题? - Antoni
@Antoni 终止反斜杠表示下一行仍然是宏的一部分,但被解释为注释的一部分,因此不会被识别。 - Gregor Hartl Watters

6

您需要通过在末尾使用\进行转义,来转义该行末尾的换行符:

#define sqr(X) \
        ((X)*(X))

2

现在已经是2021年了,我们应该真正开始向inline转变。不正确、不必要和过度使用宏会使代码膨胀,并且它们很难调试(读起来真的很难调试)。

inline void foo(x)
{
   // do whatever with x
}

如果宏确实是当务之急,那么将它们包围在 do { } while(0); 中的原因在这篇文章中有解释:为什么在宏中使用看似无意义的 do-while 和 if-else 语句?


终于有人提到了多行宏中的 do { /* code */ } while (0) 习惯用法。我认为这是需要更广泛传播的知识,因为我仍然看到有人使用简单的 { /* code */ } 编写多行宏,然后在分号上马虎。 - phetdam
终于,有人提到了 do { /* code */ } while (0) 的多行宏语法。我认为这是需要更广泛传播的知识,因为我仍然看到有人使用简单的 { /* code */ } 编写多行宏,然后在分号上变得懒散。 - phetdam

0
我们可以编写多行宏,就像函数一样,但每个语句都以"\"结尾。让我们通过示例来看看。下面是一个简单的宏,它接受用户输入的数字,并打印出输入的数字是偶数还是奇数。
#include <stdio.h>
  
#define MACRO(num, str) ({\
            printf("%d", num);\
            printf(" is");\
            printf(" %s number", str);\
            printf("\n");\
           })
  
int main(void)
{
    int num;
  
    printf("Enter a number: ");
    scanf("%d", &num);
  
    if (num & 1)
        MACRO(num, "Odd");
    else
        MACRO(num, "Even");
  
    return 0;
}

似乎是从geeksforgeeks复制的。 - Nirvana
本文介绍了在C语言中的多行宏定义。 - McKinley
这个有问题 - 宏中的 ( ) 不需要,它会创建无效的代码。 - Connor Clark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接