Требуется ли 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 из определения, но я точно не знаю,