Когда использовать внешний «C»?

#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 утилиту, которая покажет вам (неперестроенные) имена, используемые в библиотеке или исполняемом файле.