How extern "C" Prevents Name Mangling in C++

In C++, function overloading and namespaces require the compiler to alter function identifiers into unique, decorated symbols—a mechanism known as name mangling. This article explains how the extern "C" linkage specification disables this behavior, allowing C++ code to generate flat, undecorated function names that can successfully link with standard C libraries and other external binary interfaces.

The Purpose of Name Mangling in C++

C++ permits multiple functions to share the exact same name as long as their parameter lists or scopes differ, such as in function overloading, class member functions, and namespaces.

To make this possible at the machine level, the compiler modifies the function name during the compilation phase to embed details about its parameter types, calling convention, and enclosing scope. For example, a function defined as:

void calculate(int x, double y);

might be transformed into a symbol like _Z9calculateid inside the compiled object file. When the linker runs, it matches callers with definitions using these mangled symbol names.

The Interoperability Issue

The C programming language does not support function overloading. As a result, C compilers do not mangle function names; the function calculate simply produces an object symbol named calculate (or sometimes _calculate, depending on the platform's ABI).

If a C++ program attempts to call a function compiled with a C compiler without any special directives, the C++ compiler will generate a reference to the mangled symbol (e.g., _Z9calculateid). The linker will search the C object file for _Z9calculateid, fail to find it, and terminate with an "undefined reference" or "unresolved external symbol" error.

How extern "C" Works

The extern "C" directive acts as a linkage specification. It tells the C++ compiler to treat the enclosed function declarations using the standard C linkage convention rather than the C++ convention.

When a function is marked with extern "C", the compiler:

  1. Suppresses Name Decoration: It writes the function's name directly into the object file's symbol table without encoding parameter types or namespace qualifiers.
  2. Adopts the C Calling Convention: It ensures that arguments are placed on the stack or in registers according to the platform's standard C Application Binary Interface (ABI).

Because the symbol name in the generated object file remains unaltered, the linker can directly match calls from C++ code to definitions compiled in C, and vice versa.

Usage and Syntax

extern "C" can be applied to a single function declaration:

extern "C" void display_message(const char* msg);

It can also be applied to an entire block of declarations:

extern "C" {
    int open_connection(const char* host, int port);
    void close_connection(int handle);
}

To make a C header file compatible with both C and C++ compilers, developers use the __cplusplus macro. This ensures that the extern "C" directive is only read by C++ compilers, as standard C compilers do not recognize the syntax:

#ifndef MY_HEADER_H
#define MY_HEADER_H

#ifdef __cplusplus
extern "C" {
#endif

void perform_operation(int id);

#ifdef __cplusplus
}
#endif

#endif

Constraints of extern "C"

Because extern "C" strips parameter metadata from the symbol name, you cannot overload functions that are declared with C linkage. Attempting to declare two functions with the same name under extern "C" will cause a compilation error, as both would generate an identical symbol in the resulting object file.