Перейти к содержанию

Последнее обновление:2026-10-12

C++ спецификация компоновки extern

Об авторе:

Личный официальный аккаунт:

wechat

1.Различия компиляции C++ и C

В C++ часто можно встретить в заголовочных файлах функции, модифицированные extern "C" — зачем это нужно? Это нужно, чтобы код C++ линковался с функциями, определёнными в модулях на языке C.

Хотя C++ совместим с C, символы, генерируемые при компиляции функций в файлах C++, отличаются от генерируемых в C. Поскольку C++ поддерживает перегрузку функций, символы, создаваемые компилятором C++, содержат информацию о типах параметров функции, а в C — нет.

Например, после того как функция int add(int a, int b) обработана компилятором C++ и получен .o-файл, add превращается во что-то вроде add_int_int, тогда как в C это будет что-то вроде _add; иначе говоря: одна и та же функция после компиляции в C и C++ даёт разные символы.

Отсюда возникает проблема: если в C++ используются функции, реализованные на C, при компиляции и линковке произойдёт ошибка — будет сообщено, что соответствующий символ не найден. Вот здесь и пригождается extern "C": он сообщает компоновщику, что нужно искать C-символы вида _add, а не символы, промодифицированные C++.

2.Вызов C-функций из C++

Пример вызова C-функции из C++: при подключении заголовочного файла C нужно добавлять extern "C"

//add.h
#ifndef ADD_H
#define ADD_H
int add(int x,int y);
#endif

//add.c
#include "add.h"

int add(int x,int y) {
    return x+y;
}

//add.cpp
#include <iostream>
#include "add.h"
using namespace std;
int main() {
    add(2,3);
    return 0;
}

Компиляция:

//Generate add.o file
gcc -c add.c

Линковка:

g++ add.cpp add.o -o main

Без добавления extern "C" возникает ошибка:

> g++ add.cpp add.o -o main                                   
add.o:在函数‘main’中:
add.cpp:(.text+0x0): `main'被多次定义
/tmp/ccH65yQF.o:add.cpp:(.text+0x0):第一次在此定义
/tmp/ccH65yQF.o:在函数‘main’中:
add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用
add.o:在函数‘main’中:
add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用
collect2: error: ld returned 1 exit status

После добавления extern "C":

add.cpp

#include <iostream>
using namespace std;
extern "C" {
    #include "add.h"
}
int main() {
    std::cout << add(2, 3) << std::endl;
    return 0;
}

При компиляции обязательно обратите внимание: сначала сгенерируйте промежуточный файл add.o с помощью gcc.

gcc -c add.c 

Затем компиляция:

g++ add.cpp add.o -o main

Если вы используете bazel, запустите: bazel run basic_content/extern/extern_c++:main

А чтобы код на C был универсальным — то есть вызывался как из C, так и из C++ — заголовочный файл обычно оформляют так:

#ifdef __cplusplus
extern "C"{
#endif
int add(int x,int y);
#ifdef __cplusplus
}
#endif

То есть при вызове этого интерфейса из C++ вызов выполняется как вызов C-интерфейса. Такой способ избавляет программиста C++ от необходимости писать дополнительный extern C; похожим образом устроены и заголовочные файлы стандартной библиотеки — иначе почему вы можете напрямую пользоваться C-функциями из stdio.h без extern C?

Исходный код приведённого примера:

2.Вызов C++-функций из C

extern "C" в C — синтаксическая ошибка; его нужно помещать в заголовочный файл C++.

// add.h
#ifndef ADD_H
#define ADD_H
extern "C" {
    int add(int x,int y);
}
#endif

// add.cpp
#include "add.h"

int add(int x,int y) {
    return x+y;
}

// add.c
extern int add(int x,int y);
int main() {
    add(2,3);
    return 0;
}

Компиляция:

g++ -c add.cpp

Линковка:

gcc add.c add.o -o main

Исходный код приведённого примера:

Подводя итог, сформулируем правила использования: в заголовочных файлах языка C внешние функции можно объявлять только как extern; язык C не поддерживает объявление extern "C", и если в .c-файле встретится extern "C", возникнет синтаксическая ошибка компиляции. Поэтому extern "C" целиком помещают в файлы, связанные с cpp-программой, или её заголовочные файлы.

Обобщим в виде следующих схем:

(1)Вызов C-функции из C++:

//xx.h
extern int add(...)

//xx.c
int add(){

}

//xx.cpp
extern "C" {
    #include "xx.h"
}

(2)Вызов C++-функции из C

//xx.h
extern "C"{
    int add();
}
//xx.cpp
int add(){

}
//xx.c
extern int add();

Впрочем, в отличие от вызова C-интерфейса из C++, C++ действительно может вызывать скомпилированные C-функции, тогда как здесь, при вызове C++ из C, код C++ просто компилируется как код C и затем вызывается. Иными словами, C не может напрямую вызывать библиотечные функции C++.

Похожие темы

Комментарии