#c #extern-c
#c #внешний-c
Вопрос:
Я знаю, как использовать extern "C"
, но каковы условия, когда вы должны его использовать?
extern "C"
указывает компилятору C не выполнять никаких искажений имен в коде в фигурных скобках. Это позволяет вызывать функции C из C .
Например:
#include <string.h>
int main()
{
char s[] = "Hello";
char d[6];
strcpy_s(d, s);
}
Пока это нормально компилируется на VC . Но иногда это записывается как:
extern "C" {
#include <string.h>
}
Я не вижу смысла. Можете ли вы привести реальный пример, где extern "C"
это необходимо?
Комментарии:
1. Вместо взлома
<string.h>
, чтобы избежать искажения имени, используйте<cstring>
заголовок.2. @AlexandreC.: это абсолютно не очень хорошая идея использовать
<cxxx>
заголовки.<cxxx>
Заголовок не гарантирует размещение идентификаторов в глобальном пространстве имен, что приводит к нарушению кода, который полагается на идентификаторы глобального пространства имен, когда этот код перемещается в какой-либо другой компилятор. Кроме того,<cxxx>
заголовок может и на практике будет размещать идентификаторы в глобальном пространстве имен, таким образом, теоретически также нарушая код, который полагается на незагрязненное глобальное пространство имен. «В теории» слишком расплывчато; по крайней мере, один идиот сделал это. Итак, не используйте их.3. @AlfP.Steinbach: Если вы пишете C , используйте
<cxxx>
. Если вы пишете<xxx.h>
C, используйте и компилируйте свой код с помощью компилятора C. Смешивать языки — не очень хорошая идея.4. @AlexandreC.: ваш совет — это все ассоциации и никаких рассуждений. это глупо.
5. @AlfP.Steinbach: Что, если вы добавляете префикс
std::
перед каждой функцией, которую вызываете из<cxxx>
заголовка? Могут ли все еще быть проблемы с переносимостью?
Ответ №1:
Вы используете extern "C"
для предотвращения искажения имен внутри файлов заголовков и ваших объектных файлов C для библиотек или объектов, которые уже были скомпилированы без искажения.
Например, предположим, что у вас есть widget
библиотека, которая была скомпилирована с помощью компилятора C, чтобы ее опубликованный интерфейс не был искажен.
Если вы включите файл заголовка как есть в свой код, он будет считать, что имена искажены, и эти искаженные версии — это то, что вы скажете компоновщику искать.
Однако, поскольку вы будете запрашивать что-то вроде function@intarray_float_charptr
, и widget
библиотека будет только опубликована function
, вы столкнетесь с проблемами.
Однако, если вы включите его с:
extern "C" {
#include "widget.h"
}
ваш компилятор будет знать, что он должен попытаться использовать function
не искаженную версию.
Вот почему в заголовочных файлах для материалов на C, предназначенных для включения в программы на C _ или C , вы увидите такие вещи, как:
#ifdef __cplusplus
extern "C" {
#endif
// Everything here works for both C and C compilers.
#ifdef __cplusplus
}
#endif
Если вы используете компилятор C для включения этого, #ifdef
строки приведут extern "C"
к исчезновению содержимого. Для компилятора C (где __cplusplus
определено) все будет не искажено.
Комментарии:
1. почему есть второй
#ifdef __cplusplus
для?2. @user974191: это для закрывающей фигурной скобки, иначе фигурные скобки были бы несбалансированными.
Ответ №2:
Одно из очень распространенных применений extern "C"
, когда вы экспортируете функцию из библиотеки. Если вы не отключите искажение имен C , в противном случае клиентам вашей библиотеки будет очень сложно назвать вашу функцию. И аналогично, когда вы идете в другом направлении, когда вы импортируете функцию, которая была экспортирована с помощью C linkage.
Ответ №3:
Вот конкретный пример того, где что-то ломается и нуждается extern "C"
в исправлении.
module.h
:
int f(int arg);
module.c
:
int f(int arg) {
return arg 1;
}
main.cpp
:
#include "module.h"
int main() {
f(42);
}
Поскольку я смешиваю C и C , это не будет связываться (из двух объектных файлов только один будет известен f
под его искаженным именем C ).
Возможно, самый простой способ исправить это — сделать заголовочный файл совместимым как с C, так и с C :
module.h
:
#ifdef __cplusplus
extern "C" {
#endif
int f(int arg);
#ifdef __cplusplus
}
#endif
Ответ №4:
Когда вы связываетесь с библиотеками, написанными на C extern
, сообщает компилятору не украшать имена, чтобы компоновщик мог найти функции. В C имена функций и др. содержат информацию для компоновщика, например, типы и размеры аргументов, содержащиеся в имени.
Ответ №5:
Если вы создаете двоичную библиотеку A, которая предоставляет функцию, которую вы хотели бы вызвать из двоичного файла B.
Представьте, что A.dll и B — это B.exe и вы находитесь в системе Windows.
C не описывает двоичную компоновку таким образом, чтобы B знал, как вызвать A. Обычно эту проблему решают, используя один и тот же компилятор для создания A и B. Если вам нужно более универсальное решение, вы используете ключевое слово extern. Это предоставляет функцию способом C. C описывает двоичный формат, чтобы разные двоичные файлы из разных компиляторов могли взаимодействовать друг с другом.
См .: http://en.wikipedia.org/wiki/Application_binary_interface
http://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B
Ответ №6:
Если внутри вашего кода на C у вас #include
есть заголовок для внешней библиотеки (закодированной на C), и если функции там не объявлены extern "C"
, они не будут работать (вы получите неопределенную ссылку во время ссылки).
Но в наши дни люди, кодирующие библиотеки C и предоставляющие вам заголовочные файлы, как правило, знают это и часто помещают extern "C"
в свой заголовочный файл (соответствующим образом защищенный #ifdef __cplusplus
)
Возможно, лучший способ понять это — использовать (при условии, что у вас есть система Linux) nm
утилиту, которая покажет вам (неперестроенные) имена, используемые в библиотеке или исполняемом файле.