В стандарте C99 для встроенного спецификатора (6.7.4, параграф 6) говорится:
«Если все объявления области файла для функции в переводе unit включает встроенный спецификатор функции без extern, тогда определение в этой единице перевода является строковым определение. Встроенное определение не предоставляет внешнего определение функции и не запрещает внешнее определение в другой единице перевода"
Однако когда я компилирую следующий код в godbolt:
static inline int foo(int a) {
a = 1;
return a; }
int main(void){
return foo(-1);
}
, результат включает внешнее определение (должно быть с внутренней связью):
foo: ; an external definition with internal linkage
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-4], 1
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov edi, -1
call foo
pop rbp
ret
Я думаю, что определение функции foo()
в исходном коде является встроенным определением, и поэтому результат сборки не должен содержать определение, как когда мы объявляем его с помощью необработанного встроенного кода, но кажется, что нет, почему?
Обратите внимание: если мы скомпилируем следующий код в godbolt:
inline int foo(int a) {
a = 1;
return a; }
int main(void){
return foo(-1);
}
Результатом будет:
; Here foo() is omitted because inline definition
main:
push rbp
mov rbp, rsp
mov edi, -1
call foo
pop rbp
ret
Почему foo появляется в первом, но не появляется во втором, когда все определения foo являются встроенными определениями?
Я много искал в Google, и есть много вопросов о статических встроенных функциях, но ни один из них не относится к описанному выше поведению.
Возможно, это актуально: stackoverflow.com/a/34938209/4386427
Возможно это актуально: stackoverflow.com/a/3100516/4386427
an external definition with internal linkage
Откуда вы знаете его внешний вид?
Я использую уровень оптимизации O0. Вы можете просмотреть ссылку godbolt и увидеть примеры с флагами компилятора. Второй точно содержит неопределенную ссылку и это ожидаемый результат. В C99 утверждается, что встроенное определение не должно создавать внешнее определение, поэтому правильно не выводить определение foo. Я думаю, что foo в сборке внешний из-за этой страницы в cppref.
Я просмотрел страницу gnu в этом ответе, и это меня еще больше озадачило. Там написано, что «нестатическая встроенная функция всегда компилируется сама по себе обычным способом». Однако в моих примерах он не компилирует функцию foo. Позже прочитаю более внимательно.
Заявления в стандарте C о том, генерируется ли внешнее определение, касаются семантики C, то есть того, как программа будет вести себя, как видно из ее наблюдаемого поведения. Это не утверждения о том, какой язык ассемблера будет сгенерирован. Наличие определения с локальной меткой в сборке не является свидетельством нарушения семантики C.
@ericpostpishil Боже мой, я не могу поверить, что не заметил предварительных заявлений. Спасибо за ваше терпение, чтобы проверить это. Возможно, позже я решу удалить этот вопрос. Кстати, ваше мнение о наблюдаемом поведении весьма проницательно.
Предложения, которые вы цитируете, появляются после вступления «Для функции с внешней связью применяются следующие ограничения:». Хотя предложения, которые вы цитируете, не содержат ограничений, они устанавливают терминологические определения для ограничений. Я бы интерпретировал их исключительно как функцию с внешней связью. Они не применяются к функциям с внутренней связью.
Какие уровни оптимизации вы используете?