在C中使用__cplusplus未定义的行为?

时间:2017-07-05 11:18:33

标签: c

标准要求现代C ++编译器#define__cplusplus

这通常用在C和C ++都可以使用的文件中,但实现略有不同。

但是在C(和C ++)中,标识符中使用任何双下划线的行为是未定义的!

因此,如果C编译器看到类似

的内容

#ifdef __cplusplus

那么行为是未定义的吗?

4 个答案:

答案 0 :(得分:8)

  

但是在C(和C ++)中,标识符中使用任何双下划线的行为是未定义的!

你把事情搞混了。使用一个并不是未定义的行为,只是保留了名称。关键是你不能自己定义,但是使用它们很好。

来自ISO 9899:2011:

  

7.1.3保留标识符

     

- 所有以下划线开头且以大写字母或其他下划线开头的标识符始终保留供任何使用。

答案 1 :(得分:2)

使用创建自己的宏或标识符的名称会调用未定义的行为。 C11 7.1.3(强调我的):

  

7.1.3保留标识符
  1   每个标头声明或定义其关联子条款中列出的所有标识符,也可以选择   声明或定义其关联的未来中列出的标识符   库指示子条款和始终保留的标识符   用于任何用途或用作文件范围标识符    - 全部   以下划线和大写字母开头的标识符   信件或其他下划线总是保留用于任何用途。
  ...

     

2没有保留其他标识符。   如果程序在上下文中声明或定义标识符   它是保留的(除了7.1.4允许的),或定义保留   标识符为宏名称,行为未定义

但是,您当然可以自由地使用编译器/ lib提供的宏/标识符,因为它们在您应该有权访问的范围内可用。

答案 2 :(得分:1)

标准的相应部分是(强调我的):

  

7.1.3保留标识符

     

1   每个标头声明或定义其关联子条款中列出的所有标识符,并可选择声明或定义其关联的未来库方向子条款和标识符中列出的标识符,这些标识符始终保留用于任何用途或用作文件范围标识符

与Ven发布的内容有点相反,这是完全不使用它们的隐含要求,因为它们也是为了将来的目的而保留的。

7.1.3第2节中也更严格地说明了这一点:

  

不保留其他标识符。如果程序在保留它的上下文中声明或定义标识符(除了7.1.4允许的标识符),或者将保留标识符定义为宏名称,则行为是未定义的。

因此,它们被严格保留用于将来的实现,例如__cplusplus正在使用它,作为定义保留状态的第一个规则:

  

- 所有以下划线开头且以大写字母或其他下划线开头的标识符始终保留供任何使用。

但是,由于你的例子是通过标准使用所提到的保留频谱中的标识符来实现的,所以很好。

此外,您提供的示例实际上并不是声明/定义标识符,而是检查是否存在使用名称__cplusplus的预处理标记(宏),通过C标准也可以完全正常,因为它检查它是否存在,而不是声明任何东西。 如果您将自己定义为自己,则不会严格遵守C标准并导致可能的未定义行为。

答案 3 :(得分:0)

不,在这种情况下没有未定义的行为。未定义的行为意味着编译器制造商无需对受影响的资源执行任何操作。这意味着您可以自由(在此上下文中)定义要在编译器能够实现的两种语言中接受的新的非冲突下划线标识符。认为编译器库的标准库实现的内部可能充满了这样的未定义的行为,只要编译器在定义的行为部分中正确行为。在评论的情况下,宏__cplusplus在C ++中定义为 ONLY (由编译器本身),并且它作为预处理器标志指示您正在编译c ++源。将它放在#ifndef __cplusplus和以下#endif语句之间,使其在代码之间排除编译(通常,因为它会导致语法错误或不支持的功能)。通常,您会在系统中的一些/多个C / C ++标准头文件中找到以下构造(因此它们可以在C和C ++中使用):

#ifndef __cplusplus
extern "C" {
#endif
...
#ifndef __cplusplus
}
#endif

避免使用特定于C ++的extern "C" {}构造,并在普通C中引发编译器错误。

当然,如果在包含任何这些文件之前你#define __cplusplus,你会让你的C编译器大吼大叫,包括这样的头文件。您可以自己尝试一下,看看它是如何发生的:

pru.c

#define __cplusplus 20170301
#include <stdio.h>
int main()
{
    printf("Hello, world!\n");
} /* main */

然后汇编得到:

$ make pru
cc -O -pipe  pru.c  -o pru
In file included from pru.c:2:
/usr/include/stdio.h:159:1: error: expected identifier or '('
__BEGIN_DECLS
^
/usr/include/sys/cdefs.h:59:30: note: expanded from macro '__BEGIN_DECLS'
#define __BEGIN_DECLS   extern "C" {
                               ^
In file included from pru.c:2:
/usr/include/stdio.h:235:1: error: expected identifier or '('
__BEGIN_DECLS
^
/usr/include/sys/cdefs.h:59:30: note: expanded from macro '__BEGIN_DECLS'
#define __BEGIN_DECLS   extern "C" {
                               ^
pru.c:6:2: warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)' [-Wimplicit-function-declaration]
        printf("Hello, world!\n");
        ^
pru.c:6:2: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
1 warning and 3 errors generated.
*** Error code 1

Stop.
make: stopped in /home/user

认为您永远不会将标准头文件移动到另一台计算机来编译源代码。如果您尝试这样做,那么您将拥有未定义的行为,但从不尊重编译器实现。这意味着,您必须接受编译器制造商使用那些导致编译器实现细节的未定义行为,但您不能在 代码代码中执行此操作 strong>(至少如果你不想在你的程序中做不好的事情)