【问题标题】:To extern "C" or Not to extern "C" [g++ vs cl]外部“C”或不外部“C” [g++ vs cl]
【发布时间】:2019-06-19 13:18:01
【问题描述】:

我将数字食谱four1.c 与Nayuki's FFT 进行比较。两者都是 C 版本,但我使用的是 C++ 驱动程序。为了进行比较,我正在使用 CL.exe 和 g++ 将两者编译(或更恰当地说,链接)到可执行文件中。两人似乎在争论是否将 extern "C" 用于four1 函数,但似乎都不关心Nayuki 的。我为four1 制作了一个头文件,它检查_WIN32 以便适当地切换,它可以工作,但似乎是一个完全不可接受的hack。我该如何解决这个问题?

这是头文件:

#pragma once
#ifdef _WIN32
extern "C" void four1(float data[], unsigned long nn, int isign);
#else
void four1(float data[], unsigned long nn, int isign);
#endif

这就是 CL 在没有外部“C”的情况下所做的:

drvr.obj : error LNK2019: unresolved external symbol "void __cdecl four1(float * const,unsigned long,int)" (?four1@@YAXQAMKH@Z) referenced in function _main
drvr.exe : fatal error LNK1120: 1 unresolved externals

如果使用 extern "C",这就是 g++ 所做的:

/tmp/ccK1Hb2N.o: In function `main':
drvr.cpp:(.text+0x347): undefined reference to `four1'
collect2: error: ld returned 1 exit status

对 CL 有效的对 g++ 无效,对 g++ 有效的对 CL 无效。至少对于这个文件。 Nayuki 代码不存在这样的问题。

我尝试按照建议修改头文件,所以现在是 dfour1.h:

#pragma once
/* C++ needs to know that types and declarations are C, not C++.  */
#ifdef  __cplusplus
# define __BEGIN_DECLS  extern "C" {
 void dfour1(double data[], unsigned long nn, int isign);
# define __END_DECLS    }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif

g++ 很好。 CL 不是。

>cl drvr.cpp dfour1.c fft.c carrier.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

drvr.cpp
Generating Code...
Compiling...
dfour1.c
fft.c
Generating Code...
Compiling...
carrier.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:drvr.exe
drvr.obj
dfour1.obj
fft.obj
carrier.obj
drvr.obj : error LNK2019: unresolved external symbol "void __cdecl dfour1(double * const,unsigned long,int)" (?dfour1@@YAXQANKH@Z) referenced in function _main
drvr.exe : fatal error LNK1120: 1 unresolved externals

顺便说一句,如果没有头文件也会发生同样的情况,我只是添加:

extern "C" void dfour1(double data[], unsigned long nn, int isign);

到 drvr.cpp 文件,并删除头文件。 CL 仅在 extern "C" 存在时才有效,但 g++ 不存在。而删除 extern "C" 适用于 g++ 但不适用于 CL。

是的,我知道原来的头文件是错误的,这就是问题的重点。当我“正确”地做到这一点时,它不起作用,因此是帖子。当我制作了一个“不正确”的头文件来检查正在使用的编译器时,它起作用了,这是令人讨厌的部分。简单地检查 c++ 是行不通的。

【问题讨论】:

  • 条件编译检查错误,应该检查__cplusplus
  • 当然,你需要链接到实际定义函数的库或目标文件。
  • 您看到的差异可能是因为一个编译器将您的代码视为 C,而另一个编译器将其视为 C++。解决这个问题,并使用 __cplusplus 如前所述。
  • exterm "C" 用于当您想要呈现 C 兼容接口、C 调用约定并且没有 C++ 名称修饰时 - 这显然限制了您可以在接口中公开的内容(例如,没有 C++ 标准库类型,没有超载等)。如果那是你需要/想要的,那么就使用它,否则不要。就这么简单。

标签: c++ c g++ cl extern-c


【解决方案1】:

查看几乎所有系统 C 头文件,您会看到如下代码:

/* C++ needs to know that types and declarations are C, not C++.  */
#ifdef  __cplusplus
# define __BEGIN_DECLS  extern "C" {
# define __END_DECLS    }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif

__BEGIN_DECLS

// some declarations

__END_DECLS

这是在您需要接口 C 和 C++ 的您自己的标头中遵循的模式。当然,除非您不应该使用前导下划线,因为它们是为系统内容保留的。

【解决方案2】:

使用gcc编译four1.c不是g++

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 2016-05-19
    • 2019-12-22
    • 1970-01-01
    相关资源
    最近更新 更多