Зачем нам нужен extern «C» {#include } в C ++?

Почему мы должны использовать:

extern "C" { #include  } 

В частности:

    C и C ++ внешне похожи, но каждый компилируется в совершенно другой набор кода. Когда вы включаете заголовочный файл с компилятором C ++, компилятор ожидает кода на C ++. Если, однако, это заголовок С, то компилятор ожидает, что данные, содержащиеся в файле заголовка, будут скомпилированы в определенный формат – C ++ «ABI» или «Application Binary Interface», так что компоновщик задыхается. Это предпочтительнее передавать данные C ++ функции, ожидающей данные C.

    (Чтобы попасть в действительно ничтожно, ABI обычно комментирует имена своих функций / методов, поэтому вызов printf() не помещает прототип в качестве функции C, C ++ фактически генерирует код, вызывающий _Zprintf , плюс лишнее дерьмо в конце.)

    Итак: используйте extern "C" {...}; при включении заголовка ac – это просто. В противном случае у вас будет несоответствие в скомпилированном коде, и компоновщик задохнется. Однако для большинства заголовков вам даже не понадобится extern потому что большинство заголовков системы C уже учтены тем фактом, что они могут быть включены кодом C ++ и уже extern кода.

    extern «C» определяет, как должны быть указаны символы в сгенерированном объектном файле. Если функция объявлена ​​без extern «C», имя символа в объектном файле будет использоваться для обозначения имени C ++. Вот пример.

    Учитывая test.C, вот так:

     void foo() { } 

    Компиляция и перечисление символов в объектном файле дает:

     $ g++ -c test.C $ nm test.o 0000000000000000 T _Z3foov U __gxx_personality_v0 

    Функция foo на самом деле называется «_Z3foov». Эта строка содержит, среди прочего, информацию о типе типа возврата и параметров. Если вы вместо этого напишите test.C следующим образом:

     extern "C" { void foo() { } } 

    Затем скомпилируйте и посмотрите на символы:

     $ g++ -c test.C $ nm test.o U __gxx_personality_v0 0000000000000000 T foo 

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

    Обычно вы включаете заголовок внутри extern «C» {}, если код, который идет с ним, был скомпилирован компилятором C, но вы пытаетесь вызвать его из C ++. Когда вы это сделаете, вы сообщаете компилятору, что все объявления в заголовке будут использовать ссылку C. Когда вы связываете свой код, ваши файлы .o будут содержать ссылки на «foo», а не «_Z3fooblah», которые, надеюсь, совпадают с тем, что находится в библиотеке, с которой вы связываете.

    Большинство современных библиотек будут размещать охранники вокруг таких заголовков, чтобы символы были объявлены с правильной связью. например, во многих стандартных заголовках вы найдете:

     #ifdef __cplusplus extern "C" { #endif ... declarations ... #ifdef __cplusplus } #endif 

    Это гарантирует, что, когда код C ++ включает заголовок, символы в вашем объектном файле соответствуют тому, что находится в библиотеке C. Вам нужно только поставить extern «C» {} вокруг вашего C-заголовка, если он старый и уже не имеет этих охранников.

    В C ++ у вас могут быть разные объекты, которые делят имя. Например, вот список функций, названных foo :

    • A::foo()
    • B::foo()
    • C::foo(int)
    • C::foo(std::string)

    Чтобы разграничить их все, компилятор C ++ создаст уникальные имена для каждого в процессе, называемом «приписывание имен» или «украшение». Компиляторы C этого не делают. Кроме того, каждый компилятор C ++ может сделать это по-другому.

    extern «C» сообщает компилятору C ++ не выполнять каких-либо манипуляций с именем кода в фигурных скобках. Это позволяет вам вызывать функции C из C ++.

    Это связано с тем, как разные компиляторы выполняют обработку имени. Компилятор C ++ будет кастовать имя символа, экспортированного из файла заголовка, совершенно иначе, чем компилятор C, поэтому, когда вы попытаетесь установить ссылку, вы получите ошибку компоновщика, говорящую о наличии недостающих символов.

    Чтобы решить эту проблему, мы говорим компилятору C ++, чтобы он работал в режиме «C», поэтому он выполняет управление именами так же, как и компилятор C. Сделав это, ошибки компоновщика фиксированы.

    Когда мы должны его использовать?

    Когда вы связываете C-библиотеки с объектами C ++

    Что происходит на уровне компилятора / линкера, который требует от нас его использования?

    C и C ++ используют разные схемы для обозначения имен символов. Это говорит компоновщику использовать схему C при связывании в данной библиотеке.

    Как в плане компиляции / связывания это решает проблемы, которые требуют от нас использовать?

    Использование схемы именования C позволяет вам ссылаться на символы стиля C. В противном случае компоновщик попытается использовать символы стиля C ++, которые не будут работать.

    C и C ++ имеют разные правила об именах символов. Символы – это то, как линкер знает, что вызов функции «openBankAccount» в одном объектном файле, созданный компилятором, является ссылкой на эту функцию, которую вы назвали «openBankAccount» в другом объектном файле, полученном из другого исходного файла тем же (или совместимым) компилятор. Это позволяет вам сделать программу из более чем одного исходного файла, что является облегчением при работе над крупным проектом.

    В C правило очень просто, символы все равно в одном пространстве имен. Таким образом, целые «носки» хранятся как «носки», а функция count_socks хранится как «count_socks».

    Линкеры были созданы для C и других языков, таких как C, с помощью этого простого правила обозначения имен символов. Поэтому символы в компоновщике – это просто строки.

    Но в C ++ язык позволяет вам иметь пространства имен, а также polymorphism и различные другие вещи, которые противоречат такому простому правилу. Все шесть ваших полиморфных функций, называемых «добавить», должны иметь разные символы, или же неправильный будет использоваться другими объектными файлами. Это делается путем «mangling» (это технический термин) имена символов.

    При связывании кода на C ++ с библиотеками или кодом C вам нужно, чтобы extern «C» написал все на C, например файлы заголовков для библиотек C, чтобы сообщить вашему компилятору C ++, что эти имена символов не будут искалечены, а остальная часть ваш код на C ++, конечно, должен быть искалечен или он не будет работать.

    Вы должны использовать extern «C» в любое время, когда вы включаете заголовок, определяющий функции, находящиеся в файле, скомпилированном компилятором C, который используется в файле C ++. (Многие стандартные библиотеки C могут включать эту проверку в своих заголовках, чтобы сделать ее проще для разработчика)

    Например, если у вас есть проект с 3-мя файлами, util.c, util.h и main.cpp, и файлы .c и .cpp скомпилированы с помощью компилятора C ++ (g ++, cc и т. Д.), То это не ” действительно необходимо, и может даже вызвать ошибки компоновщика. Если в процессе сборки используется обычный компилятор C для util.c, тогда вам нужно будет использовать extern «C» при включении util.h.

    Случается, что C ++ кодирует параметры функции в своем имени. Вот как работает перегрузка функций. Все, что имеет тенденцию происходить с функцией C, – это добавление подчеркивания («_») к началу имени. Без использования extern «C» компоновщик будет искать функцию с именем DoSomething @@ int @ float (), когда фактическое имя функции – _DoSomething () или просто DoSomething ().

    Использование extern «C» решает указанную выше проблему, сообщая компилятору C ++, что он должен искать функцию, которая следует за соглашением об именах C, а не с C ++.

    Конструкция extern "C" {} указывает компилятору не выполнять манипуляции с именами, объявленными в фигурных скобках. Обычно компилятор C ++ «расширяет» имена функций, чтобы они кодировали информацию о типе о аргументах и ​​возвращаемом значении; это называется искаженным именем . extern "C" предотвращает искажение.

    Обычно он используется, когда код C ++ требует вызова библиотеки языка C. Он также может использоваться при экспонировании функции C ++ (например, из DLL) на клиенты C.

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

    Это используется для решения проблем с изменением имени. extern C означает, что функции находятся в «плоском» C-стиле API.

    Interesting Posts
    Давайте будем гением компьютера.