14 / 163 · C++11 · 9 min
extern "C": Language Linkage and the C ABI Boundary
extern "C" specifies C language linkage for applicable function and variable declarations so they can connect to a compatible C interface. It does not switch the function body to the C language, and it does not give arbitrary C++ types a cross-language ABI; names, calling convention, layout, and resource responsibility must each be handled separately.
In this lesson
Language Linkage Is More Than Stripping Name Mangling
A C++ compiler usually encodes parameter types and related information into the external symbol name in order to support overloading. extern "C" specifies C language linkage so the code can link with output from a compatible C toolchain; at the standard level, language linkage also concerns function types and calling conventions, and it does not prescribe a uniform cross-platform spelling of symbols.
It should appear at namespace scope. Declarations of the same entity must keep a consistent language linkage; you cannot first declare an ordinary C++ function and later restate that same function as having C linkage. The defining source file should include the interface header so both sides of the declaration are checked for a match at compile time.
The Function Body Remains C++
The implementation of a C-linkage function may still use C++ classes, templates, and RAII; it is still compiled by a C++ compiler under C++ rules. The example interface uses only int, and the function body calls a static member function of a C++ class, showing that the linkage specification does not change the language of the function body.
Putting a class inside extern C braces does not turn its member functions into an ordinary C interface. The C side does not understand references, templates, overload sets, or the library layout of std::string; export small, explicit free functions, and when necessary hide C++ objects behind opaque handles on the implementation side.
A Stable Boundary Still Needs a Protocol
Headers shared by C and C++ usually wrap the extern C declaration region with #ifdef __cplusplus so a C compiler does not see unrecognized syntax. Besides parameter types, the interface should state whether pointers may be null, buffer lengths, error codes, and which side allocates and frees resources.
C++ exceptions must not cross a C boundary that does not understand exceptions; the wrapper should catch them and convert them to the agreed error. The same C-linkage declaration also does not guarantee natural binary compatibility across architectures, compiler options, or runtimes; the actual ABI still depends on the target toolchain, and communication across processes or machines should use a serialization protocol.
Pitfalls
- You cannot build an ordinary C++ overload set from two C-linkage functions that share a name but differ in parameter types; use different names for interfaces exported to C.
- extern "C" does not specify ownership, does not perform encoding conversion, and does not automatically stop exceptions from escaping the implementation.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <iostream>
extern "C" int guide_double_small(int value);
class Calculator {
public:
static int double_small(int value) {
return (value >= 0 && value <= 100) ? value * 2 : -1;
}
};
extern "C" int guide_double_small(int value) {
return Calculator::double_small(value);
}
int main() {
assert(guide_double_small(4) == 8);
assert(guide_double_small(101) == -1);
std::cout << guide_double_small(4) << '\n';
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-extern-c.cpp -o example && ./exampleExpected result
8
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
You plan to export to C a function that returns std::string. Is adding only extern "C" enough? Propose a more appropriate boundary.
Show a reference answer
No. C cannot understand the construction, destruction, and ABI of std::string. You can export a function that accepts a caller-provided buffer and capacity, report the required length or an error code in the return value, and state whether a terminating null character is included. The C++ wrapper catches exceptions and maps them to an error result, avoiding handing standard-library objects and their deallocation responsibility across the boundary.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.