是否还需要在源文件中添加“extern C”?

我最近发现了一些代码,其中extern“C”也被添加到源文件中以用于函数。 它们也被添加到声明它们的头文件中。

我假设在头文件中添加’extern“C”就足够了。

应该在哪里添加extern“C”块?

更新:假设我正在使用CPP编译器编译我的C代码,并为头文件中的所有函数添加了外部“C”防护(即我的所有函数都在头文件中有原型),但在源文件中我没有添加相同的。 这会导致问题吗?

既然你的意思

extern "C" { ... } 

样式保护,这些声明一些函数是“C”链接,而不是“C ++”链接(它通常有一堆额外的名称装饰来支持像重载函数之类的东西)。

当然,目的是允许C ++代码与C代码接口,C代码通常在库中。 如果库的标题不是用C ++编写的, 那么它们就不会包含C ++的extern "C"保护

用C ++编写的AC标题将包含一些内容

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

确保C ++程序看到正确的链接。 但是,并非所有库都是用C ++编写的,所以有时你必须这样做

 extern "C" { #include "myclibrary.h" } 

使连接正确。 如果头文件是由其他人提供的,那么改变它是不好的做法(因为那时你不能轻易更新它),所以最好用你自己的后卫(可能在你自己的头文件中)包装头文件。

extern "C"不是(AFAIK)ANSI C,因此在没有预处理器保护的情况下不能包含在普通C代码中。

为了回应您的编辑:

如果您使用的是C ++编译器,并且在头文件中将函数声明为extern“C”,则无需在实现文件中将该函数声明为extern“C”。 从C ++标准的7.5节(强调我的):

如果同一函数或对象的两个声明指定了不同的链接规范(即,这些声明的链接规范指定了不同的字符串文字),如果声明出现在同一个转换单元中,则程序格式错误,并且如果声明出现在不同的翻译单元中,则适用一个定义规则。 除了具有C ++链接的函数之外,没有链接规范的函数声明不应位于该函数的第一个链接规范之前。 在看到明确的链接规范之后,可以在没有链接规范的情况下声明函数; 在早期声明中明确指定的链接不受此类函数声明的影响。

我不相信这是一个好的做法,因为链接规范可能会偶然发生分歧(例如,如果包含链接规范的头文件未包含在实现文件中)。 我认为最好在实现文件中明确。

他们只需要进入其他源文件包含的任何内容。

有了一些习语,你会找到包括源文件在内的人。

它们应该添加到所有文件中,并包含在其他文件中。

通常,一个不包含源文件。

你的意思是’extern c’预处理器? 它们必须在函数定义上,并且影响函数调用如何存储在已编译的二进制文件中。 如果你将编译的c ++与编译为C的c(与.cpp文件中的c相对)链接在一起,那么它才真正需要。

辩解书

这个问题已经变得更加清晰了。 这个答案解决了原始问题,当它至少是有争议的是它是否正在讨论防止多头包含在头文件中 – 这是我的答案所解决的。 显然,如果问题已经像现在一样清楚,我就不会提交这个答案。


原始答案

不,也没有必要在C代码中包含防护装置。

如果头文件’header.h’说:

 #ifndef HEADER_H_INCLUDED #define HEADER_H_INCLUDED ... #endif 

那么源文件’source.c’是完全安全的:

 #include "header.h" 

其他标题包含’header.h’也是安全的。

然而,人们注意到打开头文件并阅读它需要时间,这会减慢编译速度,因此有时人们会这样做:

 #ifndef HEADER_H_INCLUDED #include "header.h" #endif 

这意味着如果’source.c’中包含的其他标题已包含’header.h’,则不会重新处理’#include’。 (或者,如果’header.h’已经直接包含在’source.c’中,尽管这是一个愚蠢的小问题。)

因此,遇到这种情况时,很可能是尝试优化编译性能。 它很难买到它,这还不是很清楚; 现代C预处理器对这个问题非常聪明,如果可以的话,将避免重新包含文件。 而且’source.c’中的测试总是有一个错误(也许是#ifndef HEARER_H_INCLUDED),在这种情况下,测试会减慢编译速度,因为预处理器会测试不相关的条件,然后继续包含’header.h’。所有。 这是’安全’; 标题本身受到保护 – 或应该是。

如果您看到’source.c’中的代码也执行’#define HEADER_H_INCLUDED’,那么就会出现问题。 #define必须在#include之前或之后,并且都不是一般技术。

  • 如果’source.c’在包含’header.h’之前’#define HEADER_H_INCLUDED’,那么如果警卫出现在’header.h’中,则不会包含标题的内容。 如果守卫没有出现在’header.h’中,那么事情就可以了。
  • 如果’source.c’在包含’header.h’之后’#define HEADER_H_INCLUDED’,那么如果警卫出现在’header.h’中,我们将获得HEADER_H_INCLUDED的良性重新定义。 如果’header.h’不包含保护但是包含一个包含’header.h’的文件,那么毕竟不会受到多重保护。

请注意,标题的正文出现在’#define HEADER_H_INCLUDED’之后。 如果嵌套包含’header.h’,则再次保护。

“C”卫兵有两个目的:

  1. 编译代码时,将以允许非C ++编译器/链接器使用它们的方式导出函数(没有C ++名称修改等)
  2. 当C ++编译器使用您的头文件时,它将知道它应该以C方式绑定符号,这反过来将确保生成的程序将成功链接。 它们对非C ++编译器没有意义,但由于符号是在(1)中以C风格生成的,因此这是期望的效果。

由于您在实现文件中也包含带有“C”保护的标头,因此编译器可以使用有关如何在编译时创建符号的信息,编译器将以可以由a使用的方式创建符号。非C ++编译器。 因此,只需在头文件中指定extern“C” ,只要头文件也包含在实现文件中。

如果在源文件中使用extern,则不需要它们,如果它们在头文件中使用,并且该文件包含在其余源文件中。

据我记得标准,默认情况下所有函数声明都被视为“extern”,因此不需要明确指定它。 这不会使这个关键字无用,因为它也可以与变量一起使用(在这种情况下 – 它是解决链接问题的唯一解决方案)。 但是有了这些function – 是的,它是可选的。

更冗长的答案是它允许您使用在另一个源代码文件中编译的变量,但不为变量保留内存。 因此,要利用extern,您必须拥有源代码文件或库单元,其中包含顶层变量的内存空间(不在函数内)。 现在,您可以通过在其他源代码文件中定义同名的extern变量来引用该变量。

通常,应避免使用外部定义。 它们很容易导致无法管理的代码和难以定位的错误。 当然,有些例子表明其他解决方案不切实际,但很少见。 例如,stdin和stdout是映射到stdin.h中类型为FILE *的extern数组变量的宏; 此arrays的内存空间位于标准C库单元中。