Question

What `extern "C"` Does in C++

ccpplinkagename-manglingextern-c

Question

What exactly does adding extern "C" to C++ code do?

For example:

extern "C" {
    void foo();
}

What effect does this have on the function declaration, and when should it be used?

Short Answer

By the end of this page, you will understand what extern "C" means in C++, why it is used when working with C code, how it affects linkage and name mangling, and what it does not change about a function.

Concept

extern "C" tells the C++ compiler to use C linkage for the declared functions or variables.

The main reason this matters is name mangling.

What is name mangling?

C++ supports features like:

  • function overloading
  • namespaces
  • classes
  • templates

To support these features, C++ compilers often change function names in the compiled output. For example, a function like:

void foo(int);

might be turned into a symbol name that encodes its parameter types.

C does not do this in the same way. A C function named foo is usually exported simply as foo.

What extern "C" changes

When you write:

extern "C" void foo();

or:

extern "C" {
    void foo();
}

you are telling the C++ compiler:

  • do not use C++ name mangling for this declaration
  • treat this symbol as using C-style linkage

This allows C++ code to link correctly with functions compiled by a C compiler.

What it does not change

extern "C" does not mean:

  • the code is compiled as C instead of C++
  • C++ features inside the function body are disabled
  • the function becomes automatically callable from every language

It only affects linkage, especially the external symbol name and related calling compatibility rules defined by the implementation.

Why it matters

Without extern "C", a C++ compiler may generate a mangled name for foo, while a C library exports the plain symbol foo. The linker then cannot match them, causing an unresolved symbol or undefined reference error.

This is why extern "C" is commonly used in:

  • C headers included in C++ projects
  • C libraries that expose functions for C++ callers
  • plugin or FFI boundaries where plain C-compatible symbols are needed

Mental Model

Think of a compiled function name like a label on a package.

  • In C, the label is usually simple: foo
  • In C++, the label may include extra hidden details: foo__int_version (conceptually)

The linker is like a delivery worker matching package labels.

If a library exports foo, but your C++ code asks for a more detailed C++-style label, the delivery worker cannot find the package.

extern "C" says:

Use the simple C-style label for this function.

So both sides agree on the same name.

Take Quiz

Syntax and Examples

Basic syntax

Single declaration

extern "C" void foo();

Multiple declarations

extern "C" {
    void foo();
    int add(int a, int b);
}

Example: calling a C function from C++

Imagine this C library file:

/* mathlib.c */
int add(int a, int b) {
    return a + b;
}

And this header:

/* mathlib.h */
int add(int a, int b);

If you include that header in C++ code, you usually wrap it like this:

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

#include <iostream>

int main() {
    std::cout << add(2, 3) << '\n';
}

This tells the C++ compiler that add uses C linkage.

Common header pattern

To make a header usable from both C and C++, developers often write:

#ifndef MATHLIB_H
#define MATHLIB_H

#ifdef __cplusplus
extern "C" {
#endif

int add(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

Why this works

  • When compiled as C, __cplusplus is not defined, so extern "C" is ignored.
  • When compiled as C++, the declarations get C linkage.

This is the standard way to make C headers compatible with C++.

Step by Step Execution

Consider this C function:

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

And this C++ declaration:

extern "C" void foo();

What happens step by step

  1. The C compiler compiles library.c.
  2. It exports a symbol named something like foo.
  3. The C++ compiler sees extern "C" void foo();.
  4. It records that foo should use C linkage.
  5. When the C++ code calls foo(), it looks for the plain symbol foo.
  6. The linker matches the call to the C-compiled function successfully.

What if extern "C" is missing?

Suppose the C++ code is instead:

void foo();

Now the C++ compiler may generate a mangled symbol name for foo.

Step by step:

  1. The C compiler still exports plain foo.
  2. The C++ compiler expects a C++-style symbol for foo.
  3. The linker tries to match them.
  4. The names do not match.
  5. You may get a linker error such as:
    • undefined reference to foo
    • unresolved external symbol

Small traceable example

extern "C" int multiply(int a, int b);

int main() {
    int result = multiply(3, 4);
    return result;
}

Execution flow:

  • multiply(3, 4) is declared with C linkage
  • the generated call uses the C-style symbol name
  • the linker connects it to the implementation if a matching C symbol exists

Real World Use Cases

Using C libraries from C++

Many system and third-party libraries are written in C. Examples include:

  • operating system APIs
  • embedded libraries
  • cryptography libraries
  • image/audio processing libraries

C++ code uses extern "C" so those functions can be linked correctly.

Writing libraries meant for multiple languages

A library may be implemented internally in C++ but expose a small C-compatible API so that it can be called from:

  • C
  • Rust
  • Python bindings
  • Go FFI
  • other native languages

In these cases, exported functions are often declared with extern "C".

Plugin entry points

Some plugin systems expect a function with a known exported name such as:

extern "C" int plugin_init();

This avoids compiler-specific C++ mangled names.

Embedded and systems programming

Firmware, drivers, and low-level interfaces often mix C and C++ code. extern "C" helps keep interfaces stable across language boundaries.

Take Quiz

Real Codebase Usage

In real projects, extern "C" is usually not scattered randomly through application code. It appears at boundaries.

Common patterns

1. Wrapping C headers

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

This is common when integrating an older C library into a C++ project.

2. C-compatible public API

A C++ library may hide classes internally and expose simple functions:

extern "C" void* create_engine();
extern "C" void destroy_engine(void* engine);

This avoids exposing C++ ABI details directly.

3. Stable entry points

Applications or runtimes may search for functions by exact symbol name. extern "C" ensures predictable exported names.

Related patterns developers use alongside it

  • Validation: check pointers and arguments at the API boundary
  • Opaque pointers: expose void* or incomplete structs instead of C++ classes
  • Error handling: avoid throwing exceptions across C boundaries
  • Guard clauses: return error codes early for invalid input

Example:

extern "C" int process_data(const char* input) {
    if (input == nullptr) {
        return -1;
    }

    // Safe C++ implementation inside
    return 0;
}

The interface is C-compatible, but the implementation can still use C++ internally.

Common Mistakes

1. Thinking extern "C" means “compile this as C”

It does not. The file is still compiled as C++.

extern "C" void foo() {
    // This is still C++ code
}

You can still use many C++ features in the implementation, subject to normal rules.

2. Forgetting to use it when including a C header in C++

Broken example:

#include "clib.h"

If clib.h declares C functions but does not protect them for C++, linking may fail.

Better:

#ifdef __cplusplus
extern "C" {
#endif

void foo();

#ifdef __cplusplus
}
#endif

3. Trying to overload extern "C" functions

C linkage does not support C++-style overloading in the usual way.

Broken idea:

extern "C" void print(int);
extern "C" void print(double);

Because C linkage expects plain C-style symbol naming, overloading becomes problematic and is generally not allowed for practical use.

4. Assuming it solves all cross-language compatibility issues

extern "C" helps with linkage, but not everything else.

You still need to think about:

  • data types
  • struct layout
  • memory ownership
  • exceptions
  • calling conventions on some platforms

5. Exposing C++ types in a C API

Problematic example:

extern "C" std::string get_name();

This is not a good C API. C code cannot use std::string.

Better:

extern "C" const char* get_name();

Or use caller-provided buffers and explicit ownership rules.

6. Putting extern "C" around code that is not a declaration boundary

Use it for function and variable declarations meant to have C linkage, not as a general-purpose C++ feature toggle.

Comparisons

ConceptWhat it affectsTypical useSupports overloading?
Normal C++ declarationC++ linkage and name manglingRegular C++ codeYes
extern "C" declarationC linkage, usually no C++ manglingInterfacing with C or stable exported symbolsNo practical overloading
extern in C/C++Storage/linkage declaration across translation unitsReferencing global variables/functions defined elsewhereNot about C linkage by itself

extern vs extern "C"

These are related but different:

  • extern alone usually means “this name is defined elsewhere”
  • extern "C" means “use C linkage for this declaration”

Example:

extern int count;

This does not mean C linkage.

Example:

extern "C" int count;

This gives the variable C linkage.

Normal C++ API vs C API

FeatureC++ APIC API
OverloadingYesNo
Classes in interfaceYesNo
Name manglingUsually yesUsually no
Good for FFI boundariesSometimes harderUsually easier
ABI stability across compilersCan be trickyOften simpler
Take Quiz

Cheat Sheet

Quick reference

Basic forms

extern "C" void foo();
extern "C" {
    void foo();
    int add(int a, int b);
}

What it does

  • Uses C linkage
  • Usually disables C++ name mangling for the declaration
  • Helps C++ link with C code

What it does not do

  • Does not compile the code as C
  • Does not make C++ syntax invalid
  • Does not automatically make an API safe for all languages

Common use case

Wrap C headers so they work in C++:

#ifdef __cplusplus
extern "C" {
#endif

/* declarations */

#ifdef __cplusplus
}
#endif

Rules to remember

  • Use it at language boundaries
  • Do not expose C++-only types in a C API
  • Do not rely on overloading with C linkage
  • Think about memory ownership and error handling too

Typical problem it solves

Without extern "C":

  • C exports foo
  • C++ looks for mangled foo
  • linker error

With extern "C":

  • both sides agree on plain foo

FAQ

What does extern "C" mean in C++?

It means the declared function or variable uses C linkage instead of normal C++ linkage.

Does extern "C" prevent name mangling?

In practice, yes for the declared symbols. That is one of its main purposes.

Is extern "C" required when calling C from C++?

Yes, if the declarations are seen by a C++ compiler and need to match symbols produced by C code.

Can I use extern "C" with C++ member functions?

Not in the normal way for class member functions. C linkage is mainly used for free functions and variables at the interface boundary.

Can extern "C" functions be implemented in C++?

Yes. The implementation can be compiled as C++, while the function keeps C linkage.

Does extern "C" make a function callable from Python or Rust?

Not by itself. It helps create a C-compatible symbol, but you still need compatible types and a proper foreign-function interface.

Why do C headers use #ifdef __cplusplus around extern "C"?

Because extern "C" is valid in C++, not in C. The preprocessor guard makes the same header work in both languages.

Take Quiz

Mini Project

Description

Create a tiny C library and call it from a C++ program. This demonstrates the real purpose of extern "C": making symbol names match between C and C++ so the linker can connect them correctly.

Goal

Build a working example where a C++ program successfully calls functions implemented in C using extern "C".

Requirements

  • Create a C header that declares at least two functions.
  • Make the header usable from both C and C++.
  • Implement the functions in a C source file.
  • Call the functions from a C++ main program.
  • Print the returned results to confirm linking works.
Take Quiz

Keep learning

Related questions

View more