gpt4 book ai didi

具有 2 个参数问题的 C 预处理器宏

转载 作者:太空宇宙 更新时间:2023-11-04 06:55:38 25 4
gpt4 key购买 nike

我的 C 代码中有这个宏:

#define ASSERT(ret, num) \  // make sure ret === num
if (ret != num) { \
fprintf(stderr, "Error [%d] at line [%d] in function [%s]. Date: [%s] Time: [%s]\n", \
ret, __LINE__, __FUNCTION__, __DATE__, __TIME__); \
exit(ret); \
}

然后我这样调用它(所有参数都是整数):

ASSERT(errNo, MPI_SUCCESS);
ASSERT(aliveNeighbors, 8);
ASSERT(newGrid, !NULL);

我收到类似 (GCC v5.4) 的错误:

expected identifier or ‘(’ before ‘if’
if (ret != num) { \
error: stray ‘\’ in program
ASSERT(errNo, MPI_SUCCESS);
error: stray ‘\’ in program
ASSERT(aliveNeighbors, 8);
stray ‘\’ in program
ASSERT(newGrid, !NULL);

这里有什么问题吗?

最佳答案

给定宏定义:

#define ASSERT(ret, num) \  // make sure ret === num
if (ret != num) { \
fprintf(stderr, "Error [%d] at line [%d] in function [%s]. Date: [%s] Time: [%s]\n", \
ret, __LINE__, __FUNCTION__, __DATE__, __TIME__); \
exit(ret); \
}

您有许多问题,其中许多问题在您的错误消息中可见。

反斜杠-换行符拼接发生在标准 (ISO/IEC 9899:2011) §5.1.1.2 中定义的处理的第 2 阶段翻译阶段:

  1. Physical source file multibyte characters are mapped, in an implementation-defined manner, to the source character set (introducing new-line characters for end-of-line indicators) if necessary. Trigraph sequences are replaced by corresponding single-character internal representations.

  2. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. A source file that is not empty shall end in a new-line character, which shall not be immediately preceded by a backslash character before any such splicing takes place.

  3. The source file is decomposed into preprocessing tokens7) and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial comment. Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined.

  4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal character name is produced by token concatenation (6.10.3.3), the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted.

(第 5-8 阶段完成处理,但与此讨论无关。)

请注意,注释在第 3 阶段被删除,而正确的预处理发生在第 4 阶段,对标记化的输入。

宏定义的第一行是:

    #define ASSERT(ret, num) \  // make sure ret === num

这将宏的主体定义为 \ .不能在继续宏的反斜杠后放置注释,也不能使用 // … <eol>。宏主体中的样式注释。如果您在注释后添加反斜杠,它将继续注释到下一行,并且宏主体仍然是空的。您可以使用 /* … */小心评论:

    #define ASSERT(ret, num)   /* make sure ret === num */ \

这会正常工作。因为原始宏有尾随 // … <eol>评论,完成宏。第一条错误消息是:

expected identifier or ‘(’ before ‘if’
if (ret != num) { \

这是因为 if “宏定义”中的行实际上不是宏的一部分,if在它出现的上下文中不是预期的。

其他三个错误本质上是一样的:

error: stray ‘\’ in program
ASSERT(errNo, MPI_SUCCESS);

宏扩展为一个反斜杠,但在有效的 C 程序文本中不能有杂散的反斜杠,因此会出现错误消息。当出现反斜杠时,它们总是在程式化的上下文中,后面跟着特定的其他字符(数字、某些字母、某些标点符号、换行符)。

宏的主体也有问题:

  • __DATE____TIME__是代码编译(预处理)的日期和时间,而不是代码运行的日期和时间。它们很少有用,在这种情况下也没有用。如果您需要程序运行时的日期和时间信息,您将定义并调用一个函数来确定和格式化当前日期/时间。
  • __FUNCTION__不标准; __func__是标准的预定义标识符。
  • 正如所写,在某些上下文中不能安全地使用宏,例如:

    if (alpha == omega)
    ASSERT(retcode, 0);
    else if (gamma == delta)
    gastronomic_delight(retcode, gamma, alpha);

    else子句错误地与 if 相关联在(半更正)ASSERT而不是 alpha == omega测试。

将这些更改组合成一个可行的解决方案:

#define ASSERT(ret, num) \
do { \
if ((ret) != (num)) err_report(ret, __LINE__, __func__); \
} while (0)

你有:

extern _Noreturn void err_report(int err, int line, const char *func);

此函数格式化消息并报告标准错误,如果合适则确定时间和日期。它也退出(因此 _Noreturn 属性 — C11 的一个特性。您可能更喜欢 #include <stdnoreturn.h> 并使用 noreturn 而不是 _Noreturn 。您可能决定也可能不决定添加 __FILE__ 作为传递给的值函数;它需要另一个参数。

还要注意调用:

ASSERT(newGrid, !NULL);

是可疑的。它转换为 if ((newGrid) == (!0))这不是你想要的。您必须更加努力地测试非空指针:

ASSERT(newGrid != NULL, 1);

这比较了 newGrid 中的值与 NULL , 生成 1如果值为非空,并且 0如果它为空,则与 1 进行比较争论。请注意,此测试的错误编号不会有帮助 — 它将是 0 .

如果我在评论中遗漏了任何关键点,请告诉我。

关于具有 2 个参数问题的 C 预处理器宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45357079/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com