从C ++代码调用C函数


90

我有一个想从C ++调用的C函数。我无法使用“ extern "C" void foo()”这样的方法,因为C函数无法使用g ++进行编译。但是使用gcc可以很好地编译。有什么想法如何从C ++调用函数吗?


1
您能否写一些示例代码和g++错误消息
Matthieu Rouget,

7
如果使用C ++编译器进行编译,则为C ++。C代码不必使用C ++编译器进行编译。他们是不同的语言。您的代码不是有效的C ++,因此无法使用C ++编译器进行编译。
2013年

3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
自闭症

2
我的尝试:void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_

1
这应该保持开放状态,特别是因为它有很好的答案指出如何将C(而不是C ++)编译器用于C代码。
克里斯·斯特拉顿

Answers:


126

像这样编译C代码:

gcc -c -o somecode.o somecode.c

然后是这样的C ++代码:

g++ -c -o othercode.o othercode.cpp

然后使用C ++链接器将它们链接在一起:

g++ -o yourprogram somecode.o othercode.o

当您包含C函数的声明时,还必须告诉C ++编译器C头即将到来。因此othercode.cpp开始于:

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

somecode.h 应该包含以下内容:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(在此示例中,我使用了gcc,但是原理对于任何编译器都是相同的。分别以C和C ++进行构建,然后将其链接在一起。)


7
@Arne好点。某些人通过extern "C"在标头中使用来包装C中的某些C ++ #ifdef __cplusplus
放松

@Arne请参阅下面的答案。放松,如您所见,我是其中的一员;)
gx_

1
非常感谢 !这对我非常有用:)
Hesham Eraqi 2014年

我收到以下错误:错误:#337:链接规范与以前的“ foo”(在第1行声明)不兼容,现在可以编译了。谁能解释?
FaizanHussainRabbani 2015年

@FaizanRabbani,并非没有更多细节。
Falken教授2015年

61

让我收集其他答案和注释中的点点滴滴,为您提供一个示例,其中的C和C ++代码完全分开:

C部分:

foo.h

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

void foo(void)
{
    /* ... */
}

用编译gcc -c -o foo.o foo.c

C ++部分:

bar.cpp

extern "C" {
  #include "foo.h" //a C header, so wrap it in extern "C" 
}

void bar() {
  foo();
}

用编译 g++ -c -o bar.o bar.cpp

然后将它们链接在一起:

g++ -o myfoobar foo.o bar.o

原理: C代码应该是普通的C代码,#ifdef对于“也许有一天我会用另一种语言来称呼它”则不可以。如果某些C ++程序员调用您的C函数,那是他们这样做问题,而不是您的问题。而且,如果您是C ++程序员,则C头文件可能不是您的,并且您不应更改它,因此,对未拆函数名称(即extern "C")的处理属于您的C ++代码。

当然,您可能会为自己编写一个方便的C ++头文件,除了将C头文件包装到extern "C"声明中之外,它什么也不做。


7
似乎是合法的。理由+1
gx_13年

最后对此进行了完全清晰的解释。万分感谢!
丹尼尔·索塔

16

我同意Falken教授的回答,但是在Arne Mertz发表评论后,我想举一个完整的例子(最重要的部分是#ifdef __cplusplus):

somecode.h

#ifndef H_SOMECODE
#define H_SOMECODE

#ifdef __cplusplus
extern "C" {
#endif

void foo(void);

#ifdef __cplusplus
}
#endif

#endif /* H_SOMECODE */

somecode.c

#include "somecode.h"

void foo(void)
{
    /* ... */
}

othercode.hpp

#ifndef HPP_OTHERCODE
#define HPP_OTHERCODE

void bar();

#endif /* HPP_OTHERCODE */

othercode.cpp

#include "othercode.hpp"
#include "somecode.h"

void bar()
{
    foo(); // call C function
    // ...
}

然后,您按照Falken教授的指示进行编译和链接。

之所以起作用,是因为使用编译时未定义gcc__cplusplus,因此预处理后somecode.h包含的标头somecode.c如下所示:

void foo(void);

并且在使用进行编译时g++,然后__cplusplus 进行了定义,因此其中包含的标头othercode.cpp现在如下所示:

extern "C" {

void foo(void);

}

4
我不喜欢#ifdef __cplusplusC语言中的代码。C代码是较低的级别,如果有一天可能会从C ++代码中调用它,则不必费心。#ifdef如果要为用C ++编写的库提供C绑定标头,则只能在C ++代码中使用imo,反之则不行。
阿恩·默兹

2
@ Prof.Falken当然,但这是一个定义,旨在能够提供C ++代码的“向下”兼容性,而不是C代码。
阿内·默兹

1

这个答案的灵感来自Arne的理论依据是正确的情况。一个供应商编写了一个库,该库曾经支持C和C ++。但是,最新版本仅支持C。代码中留下的以下残留伪指令具有误导性:

#ifdef __cplusplus
extern "C" {
#endif

这花了我几个小时尝试用C ++进行编译。简单地从C ++调用C会容易得多。

ifdef __cplusplus约定违反了单一责任原则。使用此约定的代码试图同时做两件事:

  • (1)在C中执行一个函数-和-
  • (2)在C ++中执行相同的功能

就像试图同时用美式英语和英式英语写作一样。这不必要地导致#ifdef __thequeensenglish扳手#elif __yankeeenglish扳手#else一个无用的工具,这会使代码更难于阅读#endif到代码中。

对于简单的代码和小型库,ifdef __cplusplus约定可能适用;但是,对于复杂的库,最好选择一种或另一种语言并坚持使用。与支持两种语言相比,支持其中一种语言所需的维护较少。

这记录了我对Arne代码进行的修改,以使其可以在Ubuntu Linux上进行编译。

foo.h

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"
#include <stdio.h>

void foo(void)
{
     // modified to verify the code was called
     printf("This Hello World was called in C++ and written in C\n");
}

bar.cpp

extern "C" {
    #include "foo.h" //a C header, so wrap it in extern "C" 
}

int main() {
  foo();
  return(0);
}

生成文件

# -*- MakeFile -*-
# dont forget to use tabs, not spaces for indents
# to use simple copy this file in the same directory and type 'make'

myfoobar: bar.o foo.o
    g++ -o myfoobar foo.o bar.o 

bar.o: bar.cpp
    g++ -c -o bar.o bar.cpp

foo.o: foo.c
    gcc -c -o foo.o foo.c
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.