What is the effect of extern "C" in C++? What is the effect of extern "C" in C++? c c

What is the effect of extern "C" in C++?


extern "C" makes a function-name in C++ have C linkage (compiler does not mangle the name) so that client C code can link to (use) your function using a C compatible header file that contains just the declaration of your function. Your function definition is contained in a binary format (that was compiled by your C++ compiler) that the client C linker will then link to using the C name.

Since C++ has overloading of function names and C does not, the C++ compiler cannot just use the function name as a unique id to link to, so it mangles the name by adding information about the arguments. A C compiler does not need to mangle the name since you can not overload function names in C. When you state that a function has extern "C" linkage in C++, the C++ compiler does not add argument/parameter type information to the name used for linkage.

Just so you know, you can specify extern "C" linkage to each individual declaration/definition explicitly or use a block to group a sequence of declarations/definitions to have a certain linkage:

extern "C" void foo(int);extern "C"{   void g(char);   int i;}

If you care about the technicalities, they are listed in section 7.5 of the C++03 standard, here is a brief summary (with emphasis on extern "C"):

  • extern "C" is a linkage-specification
  • Every compiler is required to provide "C" linkage
  • A linkage specification shall occur only in namespace scope
  • All function types, function names and variable names have a language linkage See Richard's Comment: Only function names and variable names with external linkage have a language linkage
  • Two function types with distinct language linkages are distinct types even if otherwise identical
  • Linkage specs nest, inner one determines the final linkage
  • extern "C" is ignored for class members
  • At most one function with a particular name can have "C" linkage (regardless of namespace)
  • extern "C" forces a function to have external linkage (cannot make it static) See Richard's comment: static inside extern "C" is valid; an entity so declared has internal linkage, and so does not have a language linkage
  • Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved


Just wanted to add a bit of info, since I haven't seen it posted yet.

You'll very often see code in C headers like so:

#ifdef __cplusplusextern "C" {#endif// all of your legacy C code here#ifdef __cplusplus}#endif

What this accomplishes is that it allows you to use that C header file with your C++ code, because the macro "__cplusplus" will be defined. But you can also still use it with your legacy C code, where the macro is NOT defined, so it won't see the uniquely C++ construct.

Although, I have also seen C++ code such as:

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

which I imagine accomplishes much the same thing.

Not sure which way is better, but I have seen both.


Decompile a g++ generated binary to see what is going on

main.cpp

void f() {}void g();extern "C" {    void ef() {}    void eg();}/* Prevent g and eg from being optimized away. */void h() { g(); eg(); }

Compile and disassemble the generated ELF output:

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cppreadelf -s main.o

The output contains:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

Interpretation

We see that:

  • ef and eg were stored in symbols with the same name as in the code

  • the other symbols were mangled. Let's unmangle them:

    $ c++filt _Z1fvf()$ c++filt _Z1hvh()$ c++filt _Z1gvg()

Conclusion: both of the following symbol types were not mangled:

  • defined
  • declared but undefined (Ndx = UND), to be provided at link or run time from another object file

So you will need extern "C" both when calling:

  • C from C++: tell g++ to expect unmangled symbols produced by gcc
  • C++ from C: tell g++ to generate unmangled symbols for gcc to use

Things that do not work in extern C

It becomes obvious that any C++ feature that requires name mangling will not work inside extern C:

extern "C" {    // Overloading.    // error: declaration of C function ‘void f(int)’ conflicts with    void f();    void f(int i);    // Templates.    // error: template with C linkage    template <class C> void f(C i) { }}

Minimal runnable C from C++ example

For the sake of completeness and for the newbs out there, see also: How to use C source files in a C++ project?

Calling C from C++ is pretty easy: each C function only has one possible non-mangled symbol, so no extra work is required.

main.cpp

#include <cassert>#include "c.h"int main() {    assert(f() == 1);}

c.h

#ifndef C_H#define C_H/* This ifdef allows the header to be used from both C and C++  * because C does not know what this extern "C" thing is. */#ifdef __cplusplusextern "C" {#endifint f();#ifdef __cplusplus}#endif#endif

c.c

#include "c.h"int f(void) { return 1; }

Run:

g++ -c -o main.o -std=c++98 main.cppgcc -c -o c.o -std=c89 c.cg++ -o main.out main.o c.o./main.out

Without extern "C" the link fails with:

main.cpp:6: undefined reference to `f()'

because g++ expects to find a mangled f, which gcc did not produce.

Example on GitHub.

Minimal runnable C++ from C example

Calling C++ from C is a bit harder: we have to manually create non-mangled versions of each function we want to expose.

Here we illustrate how to expose C++ function overloads to C.

main.c

#include <assert.h>#include "cpp.h"int main(void) {    assert(f_int(1) == 2);    assert(f_float(1.0) == 3);    return 0;}

cpp.h

#ifndef CPP_H#define CPP_H#ifdef __cplusplus// C cannot see these overloaded prototypes, or else it would get confused.int f(int i);int f(float i);extern "C" {#endifint f_int(int i);int f_float(float i);#ifdef __cplusplus}#endif#endif

cpp.cpp

#include "cpp.h"int f(int i) {    return i + 1;}int f(float i) {    return i + 2;}int f_int(int i) {    return f(i);}int f_float(float i) {    return f(i);}

Run:

gcc -c -o main.o -std=c89 -Wextra main.cg++ -c -o cpp.o -std=c++98 cpp.cppg++ -o main.out main.o cpp.o./main.out

Without extern "C" it fails with:

main.c:6: undefined reference to `f_int'main.c:7: undefined reference to `f_float'

because g++ generated mangled symbols which gcc cannot find.

Example on GitHub.

Where is the extern "c" when I include C headers from C++?

Tested in Ubuntu 18.04.