Зачем нам нужен extern " C " {#include} в C++? [дубликат]



этот вопрос уже есть ответ здесь:



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



extern "C" {
#include <foo.h>
}


в частности:




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


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


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


705   10  

10 ответов:

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

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

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

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

данного теста.С вот так:

void foo() { }

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

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

функция foo на самом деле называется "_Z3foov". Эта строка содержит информацию о типе для возвращаемого типа и параметров, среди которых прочие вещи. Если вы вместо этого пишете тест.С такой:

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

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

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

вы получаете с рычага. Имя функции " 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++ можно иметь разные сущности, которые имеют имя. Например, вот список функций с именем фу:

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

чтобы различать их все, компилятор C++ будет создавать уникальные имена для каждого в процессе, называемом name-mangling или decorating. Компиляторы 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 правило очень просто, символы все равно находятся в одном пространстве имен. Таким образом, целое число "socks" хранится как "socks", а функция count_socks хранится как "count_socks".

компоновщики были построены для C и других языков, таких как C с этим простым правилом именования символов. Таким образом, символы в компоновщике-это просто простые строки.

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

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.