Есть ли разница между фигурными скобками или без них при использовании extern "C"?
Итак, будучи обученным Джеймсом Канзе и Локи Астари о C linkage, мне было интересно:
extern "C" int foo1 (void (*)());
extern "C" { int foo2 (void (*)()); }
После моего обучения, я думаю, должно быть, что foo1
использует только указатель на функцию с С++ linkage, а foo2
принимает только указатель на функцию с C-связью. Правильно ли я понимаю? Существуют ли конкретные ссылки в стандарте С++, которые объясняют различия в моем примере выше?
Изменить: Чтобы упростить для всех возможность следовать здесь pastebin с соответствующей частью из стандартного проекта С++ 11.
Ответы
Ответ 1
foo1 принимает указатель на функцию C, как показано в [dcl.link] 7.5p4
В спецификации привязки указанная языковая связь применяется к типы функций всех деклараторов функций, имена функций с внешняя связь и имена переменных с объявленной внешней связью в пределах спецификации связи. [Пример:
extern "C" void f1(void(*pf)(int));
//имя f1 и его тип функции имеют язык C //связь; pf является указателем на функцию C
Пример применяется непосредственно к foo1
, и добавленный акцент подчеркивает, что я считаю причиной. Списки параметров функции содержат декларатор функции для параметра, и все деклараторы функций зависят от спецификации привязки. Это относится как к фиксированным, так и к несвязанным спецификациям сцепления.
Некоторые различия, когда не используются фигурные скобки, - это то, что имена автоматически extern
, и явное использование спецификатора хранилища запрещено.
extern "C" int i; // not a definition
int main() {
i = 1; // error, no definition
}
extern "C" static void g(); // error
В качестве примера, где это имеет значение, рассмотрим заголовок, содержащий следующее:
extern "C" int a;
extern "C" double b;
extern "C" char c;
У кого-то может возникнуть соблазн изменить это на:
extern "C" {
int a;
double b;
char c;
}
Но это было бы неправильно, потому что это превращает объявления в определения. Вместо этого правильный код с использованием extern "C" {}
:
extern "C" {
extern int a;
extern double b;
extern char c;
}
Ответ 2
Скобки используются, когда у вас много деклараций и определений. Часто вы можете увидеть начало и конец в файлах заголовков для кода C
, который можно использовать в C++
#ifdef __cplusplus
extern "C" {
#endif
// C stuff here to be available for C++ code
#ifdef __cplusplus
}
#endif
Я могу порекомендовать прочитать про "название mangling" http://en.wikipedia.org/wiki/Name_mangling extern "C"
- это ключ к возврату к C
соглашениям об именах ссылок.
Ответ 3
extern "C" int foo1 (void (*)());
extern "C" { int foo2 (void (*)()); }
Те же. Основная причина использования фигурных скобок заключается в том, что у вас есть несколько функций, например:
extern "C" int foo1 (void (*)());
extern "C" int foo2 (void (*)());
extern "C" int foo3 (void (*)());
extern "C" int foo4 (void (*)());
который можно записать проще:
extern "C" {
int foo1 (void (*)());
int foo2 (void (*)());
int foo3 (void (*)());
int foo4 (void (*)());
}
Кроме того, если вы пытаетесь создать один заголовочный файл, который работает как с C, так и с С++, вы можете записать это как:
#ifdef __cplusplus
extern "C" {
#endif
int foo1 (void (*)());
int foo2 (void (*)());
int foo3 (void (*)());
int foo4 (void (*)());
#ifdef __cplusplus
}
#endif
P.S. Я не знаю каких-либо компиляторов, где есть разница между "С++ linkage" или "C linkage" для указателей функций. Когда мы говорим о связях C или С++, мы говорим о том, как имя искажается компилятором. Для указателя функции вы передаете указатель, поэтому имя не имеет значения. Важно, чтобы вызывающее соглашение было одинаковым, но обычно оно одинаково для C и С++, поскольку люди свободно смешивают эти языки.