Подтвердить что ты не робот

Является ли extern "C" обязательным только для объявления функции?

Я написал функцию C++, которую мне нужно вызывать из программы на Си. Чтобы сделать его вызываемым из C, я указал extern "C" в функции объявление. Затем я скомпилировал код C++, но компилятор (Dignus Systems/C++) сгенерировал искаженное имя для функции. Так что, по-видимому, это не чтило extern "C".

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

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

Вот пример, чтобы продемонстрировать это:

/* ---------- */
/* "foo.h"    */
/* ---------- */

#ifdef __cplusplus
extern "C" {
#endif

/* Function declaration */
void foo(int);

#ifdef __cplusplus
}
#endif

/* ---------- */
/* "foo.cpp"  */
/* ---------- */

#include "foo.h"

/* Function definition */
extern "C"               // <---- Is this needed?
void foo(int i) {
  // do something...
}

Моя проблема может быть связана с неправильным написанием чего-либо или я обнаружил ошибку компилятора. В любом случае, я хотел проконсультироваться со stackoverflow, чтобы убедиться, что я знаю, какой с технической точки зрения "правильный" путь.

4b9b3361

Ответ 1

"extern "C"" не должно требоваться для определения функции, если оно есть в объявлении, и это уже видно при компиляции определения. Стандарт конкретно гласит (спецификации сцепления 7.5/5):

Функция может быть объявлена без спецификации связи после того, как явная спецификация связи была замечена; связь, явно указанная в предыдущем объявлении, не затрагивается таким объявлением функции.

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

Ответ 2

Edit:
Похоже, я неправильно понял этот вопрос. В любом случае, я пробовал:


// foo.cpp
/* Function definition */

#include "foo.h"

void foo(int i) {
 //do stuff
}
void test(int i)
{
// do stuff
}

// foo.h
#ifdef __cplusplus
extern "C" {
#endif

/* Function declaration */
void foo(int);

#ifdef __cplusplus
}
#endif

void test(int);

Использование команды nm для просмотра символов из скомпилированного файла:


linuxuser$ nm foo.o
00000006 T _Z4testi
         U __gxx_personality_v0
00000000 T foo

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

Ответ 3

extern "C" вокруг определения не требуется. Вы можете уйти, просто положив его на объявление. Одна заметка в вашем примере...

#ifdef __cplusplus
extern "C" {
#endif

/* Function declaration */
void foo(int);

#ifdef __cplusplus
}
#endif

Ваш код ищет макрос препроцессора "__cplusplus".

<ы > Хотя это обычно реализуется, в зависимости от вашего компилятора, это может быть или не быть определено. В вашем примере вы также используете extern "C" вокруг объявления, но там вы не проверяете макрос "__cplusplus", поэтому я подозреваю, что он сработал, как только вы это сделали. С >

См. комментарии ниже. Стандартный С++ требует, чтобы макрос __cplusplus определялся препроцессором.

Ответ 4

Просто столкнулся с такой ситуацией... Не приятный опыт.

В одном из моих файлов c было объявлено следующее:

void unused_isr(void) {}
void ADC_IRQHandler(void)     __attribute__ ((weak, alias("unused_isr"))); 

Далее где-то в файле cpp я определил:

void ADC_IRQHandler(void) {                                                                                  
    ...
}

И я забыл изменить форвардную декларацию на:

void ADC_IRQHandler(void);

Мне потребовалось некоторое время, прежде чем я понял, что делаю все правильно в отношении преобразования AD, но мне не удалось добавить выражение "extern C" в определение!

extern "C" void ADC_IRQHandler(void) {                                                                                  
    ...
}

Только мои два цента, почему при определенных обстоятельствах может быть полезно иметь привычку добавлять его в определение.

Ответ 5

Я думаю, что это нужно разъяснить здесь, так как у меня просто была аналогичная проблема, и мне потребовалось некоторое время, чтобы понять это в моей голове, только Брукс Мозес коснулся этого должным образом, и я думаю, что это должно быть указано более ясно...

В итоге заголовок может вас отбросить, все компиляторы видят файл cpp, и если заголовок не включен в extern "C" обратно в ваш cpp (что я обычно вижу), то extern "C" должен быть в файле cpp где-нибудь (либо в определении, либо в другом объявлении), поэтому компилятор CXX может знать, чтобы сделать его с помощью C linkage, компилятор не заботится о заголовке, только компоновщик.

Ответ 6

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

Теперь, если у вас есть объявление extern-C, которое видно из единицы перевода, в которой существует определение, вы можете уйти с оставлением extern-C из определения, но я не знаю это точно.