外部链接和 »extern "C"« 块

6

我有一个int ID,我想在C++中定义并使其可用于C语言链接(为简单起见而构造的情况):

/* i.h */
#ifdef __cplusplus
extern "C" {
#endif
        extern int ID;
#ifdef __cplusplus
}
#endif

以下是使用 int 的 C 和 C++ 程序:

/* m.cpp */
#include "i.h"
#include <iostream>
int main() { std::cout << ID << std::endl; }

/* m.c */
#include "i.h"
#include <stdio.h>
int main() { printf("%d\n", ID); }

现在我想知道extern "C"和/或extern的语法。这里展示了如何定义和不能定义int ID:

/* i.cpp */
//                     const int ID = 88;   // no C linkage, obviously, LNK2019/1120
// extern "C"          const int ID = 88;   // provides C linkage
// extern "C" {        const int ID = 88; } // no C linkage, surprisingly, LNK2019/1120
// extern "C" { extern const int ID = 88; } // C linkage restored

编译:

cl /nologo /W4 m.cpp i.cpp /MD /EHsc
cl /nologo /W4 m.c   i.cpp /MD

我不明白使用带有 {block}extern "C" 时的语法。我需要重复使用 extern,而对于没有块的形式 extern "C",则不需要这样做。这只是一种语法怪异还是还有更多的东西呢?
我在 Dale Rogerson 的书《Inside COM》第98页上遇到了这个问题。有一个代码清单,其中包含嵌套的 extern 和一个旨在澄清的注释(但我不理解)。
#include <objbase.h>
extern "C"{
  extern const IID IID_IX = {0x32bb8320, 0x41b, 0x11cf, ...};
  // The extern is required to allocate memory for C++ constants.
}

有人能解释一下内部的extern吗?

看了第98页的例子,我相信链接器会迭代地解析链接。IID_IX将在内部具有C++链接,但在外部它们将以C链接可见。 - Andro
1
这是一种“让我们重用一个含义模糊的关键词”的语言设计方法的示例。 - molbdnilo
3个回答

5
内部的extern表示预期的含义:“符号在其他地方定义,具有外部链接,在本单元中不要为其分配空间(除非在此处定义)”。
外部的extern "C" { ... }语法可能有点误导,它只告诉编译器“如果您在此块内创建任何具有外部链接的符号名称,请使用传统的C命名(C语言链接)而不是C ++名称混淆(C ++语言链接)”。但它并没有指定块内的所有内容都是“在其他地方定义”的(具有外部链接)-它们仍然使用默认的内部链接进行声明。这就是为什么你需要内部的extern
一行代码的变体extern "C" <variable declaration>只是一个简写,因为如果您关心其跨单元符号名称,则可能希望定义外部变量。
(作为副注,我也会在i.cpp中包含i.h,这样我就不必记住在实现中再次使用extern "C"了。)

3
以下是int ID的定义方式:
小心!extern "C"在不同的上下文中具有不同的影响。
根据N3797 §7.5/7:

A declaration directly contained in a linkage-specification is treated as if it contains the extern specifier (7.1.1) for the purpose of determining the linkage of the declared name and whether it is a definition. Such a declaration shall not specify a storage class.

extern "C" int i; // declaration
extern "C" {
    int i;           // definition
}

因此,在您的片段中,第二行只是声明,而第三行也是定义。这改变了链接规范对声明产生的影响 - 不能给在C ++翻译单位中定义的名称提供C链接。您必须将其作为纯声明。


你说我代码片段中的第二行只是一个声明,但是 MSVC(版本 18.00.30501 für x86)编译它时,初始化值(88)被打印在屏幕上,这似乎与它仅仅是一个声明的概念相矛盾,至少就 MSVC 而言。 - Lumi
请说英语。 - BoltClock
1
@BoltClock,请恢复他和我发表的评论,我不喜欢这样霸道的编辑。 - Lumi
4
如果只有会说德语的人才能理解信息,我认为这样做没有意义(如果评论只是为了你们两个人,那么对其他人来说会非常干扰)。不管你是否喜欢,帖子和评论应该用英语编写,没有例外。请翻译此内容。 - BoltClock
1
我严重怀疑你的干预并没有改善任何事情,BoltClock - 但无论如何。 正如我所说,VC++肯定有自己关于链接的规定,因此你应该在他们的论坛上提问(或阅读手册)。 - Columbo

3
问题在于constextern相互冲突。const的意思是(除其他含义外),“使该定义内部化,除非有显式的extern”。当定义在extern "C"块中时,在定义上没有直接的extern,因此来自const的“隐式内部化”占优势。其他定义都在定义上直接使用了extern,因此变成了外部定义。

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