是否使用extern "C" [g++ vs cl]

3
我正在比较Numerical Recipes four1.c和Nayuki's FFT。两个都是C版本,但我正在使用C++驱动程序。为了进行比较,我将它们都编译(或更确切地说,链接)成一个可执行文件,使用CL.exe和g++。它们似乎在争夺是否应该对four1函数使用extern“C”,但都不关心Nayuki的问题。我为four1制作了一个头文件,检查_WIN32以适当地进行切换,这有效,但似乎是完全不可接受的黑客行为。我该如何解决这个问题?
这是头文件:
#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

这是没有使用extern "C"的CL的操作:

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
可以正常编译,而则不行。
>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文件中删除。只有在extern "C"存在时,CL才起作用,但是g++不起作用。而删除extern "C"可以与g++一起使用,但不能与CL一起使用。
是的,我知道原始头文件是错误的,这就是问题的关键。当我以“正确”的方式操作时,它不起作用,因此发布了帖子。当我制作一个“不正确”的头文件来检查正在使用的编译器时,它起作用了,这很让人烦恼。简单地检查c ++无法解决问题。

4
条件编译检查有误,应该检查__cplusplus而不是其他。 - Some programmer dude
当然,您需要链接到包含实际定义该函数的库或对象文件。 - Some programmer dude
你看到的差异可能是因为一个编译器将你的代码视为C,而另一个则视为C++。解决这个问题,并像已经说明的那样使用__cplusplus。 - john
4
exterm "C" 用于当您想要展示与 C 兼容的接口、C 调用约定和没有 C++ 名称修饰时 - 这显然会限制您可以在接口中公开的内容(例如,没有 C++ 标准库类型、无法重载等)。如果这正是您需要或想要的,那么就使用它;否则就不要使用。就这么简单。 - Jesper Juhl
2个回答

7

查看几乎任何系统的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++接口,那么可以按照这种方式进行。当然,你不应该使用前导下划线,因为它们被系统保留。



1
使用 gcc 编译 four1.c,而非 g++

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