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

Есть ли разница между фигурными скобками или без них при использовании extern "C"?

Итак, будучи обученным Джеймсом Канзе и Локи Астари о C linkage, мне было интересно:

extern "C" int foo1 (void (*)());
extern "C" { int foo2 (void (*)()); }

После моего обучения, я думаю, должно быть, что foo1 использует только указатель на функцию с С++ linkage, а foo2 принимает только указатель на функцию с C-связью. Правильно ли я понимаю? Существуют ли конкретные ссылки в стандарте С++, которые объясняют различия в моем примере выше?

Изменить: Чтобы упростить для всех возможность следовать здесь pastebin с соответствующей частью из стандартного проекта С++ 11.

4b9b3361

Ответ 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 и С++, поскольку люди свободно смешивают эти языки.