Требуется ли extern "C" только для объявления функции?

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

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

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

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

/* ---------- */
/* "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...
}

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

6 ответов

Решение

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

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

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

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


// 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, написанный без внешнего "C", был бы непригодным для использования в программах на C++.

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

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

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

Следующее было заявлено в одном из моих 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) {                                                                                  
    ...
}

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

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

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

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

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

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

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

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

Другие вопросы по тегам