форвардная декларация с использованием extern (в контексте C/C++)

63
7

Я был смущен, следует ли использовать extern для форвардных деклараций функций в C. Каждый сценарий - это отдельный файл.c/.cpp. Я понял из рассмотрения этого вопроса - Внешняя связь const в C, что если все файлы.c, я не должен использовать extern для прямого объявления независимо от того, определена ли функция в том же файле или нет.

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

спросил(а) 2021-01-25T19:12:59+03:00 4 месяца, 2 недели назад
1
Решение
78

В языке C есть такая вещь, как extern inline, который имеет особое значение, и в котором явное extern делает разницу.

Помимо этого, никогда не возникает смысла указывать явный extern в объявлениях функций, поскольку функции в C и C++ всегда имеют внешнюю связь по defualt.

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

ответил(а) 2021-01-25T19:12:59+03:00 4 месяца, 2 недели назад
63

Я думаю, что когда объявляемая функция определена на другом языке, чем вызов одного, требуется extern.

Я должен предупредить вас, что ваш вопрос плохо сформулирован. Я уверен, что вас интересует, когда использовать

extern "C" int foo(int);

в C++, но я не уверен, и я должен надеяться, что я не буду тратить свое время.

Пусть различают компилятор и компоновщик. Я оставляю некоторые детали, но ничего не влияет на ответ на ваш вопрос.

Компилятор использует форвардное объявление. При объявлении функции вы даете компилятору информацию о том, как он используется. Вы объявляете функцию F, и когда компилятор работает с использованием F, он знает, что делать. (В дни K & R, в отсутствие объявления, компилятор использовал значения по умолчанию, иногда приводящие к веселым результатам. Поэтому теперь они являются обязательными как для C, так и для C++.)

Обычно передним объявлением функции является функциональный прототип: он предоставляет типы параметров. В C это не является строго необходимым. Вы можете написать

int foo();

который говорит, что компилятор foo - это функция, возвращающая int, но не параметры, которые она принимает. Затем вы становитесь ответственным за то, чтобы получить право, потому что компилятор не может проверить.

Объявление extern - будь то функции или переменной - уведомляет компилятор о символе и его типе и говорит, что определение будет предоставлено другим модулем. Компилятор оставляет заполнитель для компоновщика для заполнения позже.

Если вы посмотрите на man-страницу getopt (3), например, вы увидите, что optarg объявлен extern; он определен в библиотеке времени выполнения C, но вы можете использовать его, потому что он был объявлен.

Теперь мы переходим к компоновщику и extern "C" C++.

Для компоновщика модуль предоставляет символы. Глобальные переменные и функции, не объявленные static имеют внешнюю связь, то есть они могут использоваться другими модулями.

В C есть соответствие 1:1 между именами функций и внешними символами. Это символ. Например, getopt (3) имеет символ с тем же именем в стандартной библиотеке C, libc:

$ nm -g $(find /usr/lib/ -name libc.a) 2>/dev/null | grep 'T getopt' 
0000000000001620 T getopt
0000000000000000 T getopt_long
0000000000000040 T getopt_long_only

В C++ имя не является символом. Функция C++ может быть перегружена; одно и то же имя может представлять разные функции с различными параметрами. Компилятор создает символ, который кодирует типы параметров. Для сравнения:

$ echo 'int foo(int foo) { return foo * foo; }' > a.c && cc  -c a.c && nm a.o
0000000000000000 T foo
$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T _Z3fooi

Кодирование имени символа часто называют изменением имени. нм (1) имеет функцию демпфирования:

$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm -C a.o
0000000000000000 T foo(int)

Обратите внимание, что рядом с именем появляется тип параметра.

В C++ extern "C" объявляет или определяет функцию как функцию C. Здесь определение:

$ echo 'extern "C" int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T foo

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

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

Надеюсь, это ответит на ваш вопрос.

ответил(а) 2021-01-25T19:12:59+03:00 4 месяца, 2 недели назад
45

По умолчанию функции имеют внешнюю связь, что означает, что

extern int foo(void);

а также

int foo(void);

имеют точно такое же значение. Это справедливо для всех объявлений функций, за исключением тех, которые связаны с static в некотором роде.

В частности, наличие или отсутствие extern не влияет на проверку типов и не зависит ли функция от другого на другом языке.

ответил(а) 2021-01-25T19:12:59+03:00 4 месяца, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема