从 C + + 代码中调用 C 函数

我有一个 C 函数,我想从 C + + 调用它。我不能使用“ extern "C" void foo()”类型的方法,因为 C 函数未能使用 g + + 进行编译。但是它使用 gcc 编译得很好。有什么办法可以从 C + + 调用这个函数吗?

116733 次浏览

Compile the C code like this:

gcc -c -o somecode.o somecode.c

Then the C++ code like this:

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

Then link them together, with the C++ linker:

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

You also have to tell the C++ compiler a C header is coming when you include the declaration for the C function. So othercode.cpp begins with:

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

somecode.h should contain something like:

 #ifndef SOMECODE_H_
#define SOMECODE_H_


void foo();


#endif


(I used gcc in this example, but the principle is the same for any compiler. Build separately as C and C++, respectively, then link it together.)

I agree with Prof. Falken's answer, but after Arne Mertz's comment I want to give a complete example (the most important part is the #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
// ...
}

Then you follow Prof. Falken's instructions to compile and link.

This works because when compiling with gcc, the macro __cplusplus is not defined, so the header somecode.h included in somecode.c is like this after preprocessing:

void foo(void);

and when compiling with g++, then __cplusplus is defined, and so the header included in othercode.cpp is now like that:

extern "C" {


void foo(void);


}

Let me gather the bits and pieces from the other answers and comments, to give you an example with cleanly separated C and C++ code:

The C Part:

foo.h:

#ifndef FOO_H
#define FOO_H


void foo(void);


#endif

foo.c

#include "foo.h"


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

Compile this with gcc -c -o foo.o foo.c.

The C++ Part:

bar.cpp

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


void bar() {
foo();
}

Compile this with g++ -c -o bar.o bar.cpp

And then link it all together:

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

Rationale: The C code should be plain C code, no #ifdefs for "maybe someday I'll call this from another language". If some C++ programmer calls your C functions, it's their problem how to do that, not yours. And if you are the C++ programmer, then the C header might not be yours and you should not change it, so the handling of unmangled function names (i.e. the extern "C") belongs in your C++ code.

You might, of course, write yourself a convenience C++ header that does nothing except wrapping the C header into an extern "C" declaration.

This answer is inspired by a case where Arne's rationale was correct. A vendor wrote a library which once supported both C and C++; however, the latest version only supported C. The following vestigial directives left in the code were misleading:

#ifdef __cplusplus
extern "C" {
#endif

This cost me several hours trying to compile in C++. Simply calling C from C++ was much easier.

The ifdef __cplusplus convention is in violation of the single responsibility principle. A code using this convention is trying to do two things at once:

  • (1) execute a function in C -- and --
  • (2) execute the same function in C++

It's like trying to write in both American and British English at the same time. This is unnecessarily throwing an #ifdef __thequeensenglish spanner #elif __yankeeenglish wrench #else a useless tool which makes the code harder to read #endif into the code.

For simple code and small libraries the ifdef __cplusplus convention may work; however, for complex libraries it is best to pick one language or the other and stick with it. Supporting one of the languages will take less maintenance than trying to support both.

This is a record of the modifications I made to Arne's code to get it to compile on 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

# -*- 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