符合标准的 C assert() 可以多次评估其参数吗?

May a standards-compliant C assert() evaluate its argument multiple times?

标准 C assert(e) 宏是否允许多次计算 e? C++11 或更高版本呢?我在 the Open Group spec, and the answer isn't apparent to me from some searching (1, 2).

中看不到任何保证

上下文:func() 可以在 assert(func() != NULL) 中多次调用吗?

是的,出于其他原因我已经知道这是一个坏主意:正如 the glibc manual 指出的那样,如果 NDEBUG 是定义。但是,假设 NDEBUG 未定义 ,是否可以保证 maximume 被评估?

this one提示的问题。

C 标准说

在C11标准(ISO/IEC 9899:2011)中,§7.1.4 库函数的使用说:

Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow: …

Any invocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once, fully protected by parentheses where necessary, so it is generally safe to use arbitrary expressions as arguments.186) Likewise, those function-like macros described in the following subclauses may be invoked in an expression anywhere a function with a compatible return type could be called.187)

186) Such macros might not contain the sequence points that the corresponding function calls do.

187) Because external identifiers and some macro names beginning with an underscore are reserved, implementations may provide special semantics for such names. For example, the identifier _BUILTIN_abs could be used to indicate generation of in-line code for the abs function. Thus, the appropriate header could specify

#define abs(x) _BUILTIN_abs(x)

for a compiler whose code generator will accept it. In this manner, a user desiring to guarantee that a given library function such as abs will be a genuine function may write

#undef abs

whether the implementation’s header provides a macro implementation of abs or a built-in implementation. The prototype for the function, which precedes and is hidden by any macro definition, is thereby revealed also.

§7.2 诊断 <assert.h> 中的序言说:

The assert macro shall be implemented as a macro, not as an actual function. If the macro definition is suppressed in order to access an actual function, the behavior is undefined.

和第 7.2.1.1 节 assert 说:

The assert macro puts diagnostic tests into programs; it expands to a void expression. When it is executed, if expression (which shall have a scalar type) is false (that is, compares equal to 0), the assert macro writes information about the particular call that failed (including the text of the argument, the name of the source file, the source line number, and the name of the enclosing function — the latter are respectively the values of the preprocessing macros __FILE__ and __LINE__ and of the identifier __func__) on the standard error stream in an implementation-defined format.191) It then calls the abort function.

191) The message written might be of the form:
Assertion failed:expression, functionabc, filexyz, linennn.

标准的可能解释

标准的废话就这么多了——它在实践中是如何转化的?

很大程度上取决于语句的解释:

  • 作为宏实现的库函数的任何调用都应扩展为对其每个参数求值一次的代码

如果assert被认为是通过宏实现的函数,那么它的参数应该只计算一次(转换为字符串是一个不计算表达式的编译时操作)。

如果assert被视为'not a function'(因为它明确地是一个宏),那么引用的限制不一定适用于它。

在实践中,我确信 assert 的表达式参数应该只计算一次(并且只有在 [=16= 时未定义 NDEBUG ] header 最后被包含在内)——所以我认为它是被约束的,就好像它是一个通过宏实现的函数。我还认为任何以表达式被评估两次的方式实现 assert 的实现都是有缺陷的。我不确定引用的 material 是否支持这一点,但它是我在标准中所知道的所有相关 material。