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

Множественное определение встроенной функции

Я прочитал некоторые сообщения, связанные с этой темой, но не смог полностью разобраться со своими сомнениями. Это может быть очень наивный вопрос.

У меня есть файл заголовка inline.h и два блока перевода main.cpp и tran.cpp.

Подробности кода приведены ниже

inline.h

#ifndef __HEADER__
#include <stdio.h>
extern inline int func1(void)
{ return 5; }

static inline int func2(void)
{ return 6; }

inline int func3(void)
{ return 7; }
#endif

main.c

#define <stdio.h>
#include <inline.h>
int main(int argc, char *argv[])
{
    printf("%d\n",func1());
    printf("%d\n",func2());
    printf("%d\n",func3());
    return 0;
}

tran.cpp

//(note that the functions are not inline here)
#include <stdio.h>
int func1(void)
{ return 500; }

int func2(void)
{ return 600; }

int func3(void)
{ return 700; }

Вышеприведенный код компилируется в g++, но не компилируется в gcc (даже если вы вносите изменения, связанные с gcc, как изменение кода на .c, не используя файлы заголовков С++ и т.д.). Отображаемая ошибка представляет собой "дублирующее определение встроенной функции - func3".

Можете ли вы пояснить, почему эта разница присутствует во всех компиляторах?

Кроме того, когда вы запускаете программу (g++ compiled), создавая два отдельных блока компиляции (main.o и tran.o) и создавая исполняемый файл a.out, полученный результат:

500
6
700

Почему компилятор выбирает определение функции, которая не является встроенной. Фактически, поскольку #include используется для "добавления" встроенного определения, которое я ожидал 5,6,7 в качестве вывода. Мое понимание было во время компиляции, поскольку встроенное определение найдено, вызов функции будет "заменен" встроенным определением функции.

Не могли бы вы подробно рассказать мне о процессах компиляции и связывания, которые приведут нас к выводу 500,6,700. Я могу понять выход 6.

4b9b3361

Ответ 1

Этот ответ разделен на следующие разделы:

  • Как воспроизвести проблему duplicate definition of inline function - func3 и почему.
  • Почему defintion func3 является дубликатом вместо func1.
  • Почему он компилируется с помощью g++

Как создать дублирующее определение встроенной функции - проблема func3

Проблема может быть успешно воспроизведена

  • Переименуйте tran.cpp в tran.c
  • Скомпилировать с помощью gcc -o main main.c tran.c

@K71993 на самом деле компилируется с использованием старой встроенной семантики gnu89, которая отличается от C99. Причина переименования tran.cpp в tran.c заключается в том, чтобы сообщить драйверу gcc рассматривать его как C вместо источника C++.


Почему определение func3 является дубликатом вместо func1.

встроенная семантика GNU 89

Следующий текст цитируется в Документ GCC: встроенная функция работает так же быстро, как и макрос, объясняет, почему func3 - это дублирующее определение вместо func1, поскольку func3 (вместо func1) является внешним видимым символом (в встроенной семантике GNU89)

Когда встроенная функция не является статической, компилятор должен предположить, что могут быть вызовы из других исходных файлов; поскольку глобальный символ может быть определен только один раз в любой программе, функция не должна определяться в других исходных файлах, поэтому вызовы в ней не могут быть интегрированы. Поэтому нестатическая встроенная функция всегда компилируется сама по себе обычным способом.

Если вы укажете как встроенные, так и внешние выражения в определении функции, то определение используется только для встраивания. Ни в коем случае функция не скомпилирована сама по себе, даже если вы явно ссылаетесь на ее адрес. Такой адрес становится внешней ссылкой, как будто вы только объявили функцию и не определили ее.

Встроенная семантика C99

Если скомпилировано со стандартом C99, то есть gcc -o main main.c tran.c -std=c99, компоновщик будет жаловаться, что определение func1 является дубликатом из-за того, что extern inline в C99 является внешним определением, как указано в других сообщениях и комментариях.

Также см. этот отличный ответ о семантических различиях между GNU89 inline и C99 inline.

Почему он компилируется с помощью g++.

При компиляции с g++ исходная программа рассматривается как источник C++. Поскольку func1, func2 и func3 определены в нескольких единицах перевода, а их определения различны, Одно правило определения С++ нарушается. Поскольку компилятор не требуется генерировать дигностическое сообщение, когда определения охватывают несколько единиц перевода, поведение undefined.

Ответ 2

Ошибка компиляции заключается в том, что существует дублирующее определение func1();

Поскольку func1() определяется с использованием extern inline, он будет производить внешнее определение.

Однако в tran.c также есть внешнее определение, которое вызывает множественную ошибку определения.

Однако func2() и func3() не производят внешнего определения, следовательно, ошибки переопределения.

Возможно, вы захотите посмотреть здесь http://www.greenend.org.uk/rjk/2003/03/inline.html.

Также обратите внимание, что С++ и c обрабатывают встроенные функции по-разному, и даже в c, разные стандарты (c89 против c99) обрабатывают встроенные функции по-разному.

Ответ 3

Возможно, вам стоит опубликовать фактический код. Отсканированные фрагменты не компилируются:

  • inline.h имеет extern inline int func1(void) Это не имеет никакого смысла.
  • main.h имеет #define <stdio.h> Я думаю, вы имели в виду include вместо этого.

Как только я исправил их и скомпилировал с помощью gcc, он скомпилировался отлично, и я получил следующий вывод

5
6
7

Когда я компилирую с g++, я получаю этот вывод:

5
6
700

Это происходит потому, что func3() не является статичным в inline.h

Ответ 4

Ваш код недействителен с точки зрения С++, поскольку он явно нарушает правило одного определения. Единственная причина, по которой вам удалось скомпилировать его с помощью компилятора С++, - это ошибка проверки ошибок в вашем компиляторе С++ (это одна из тех частей ODR, где "не требуется диагностика" ).

Ваш код недействителен C, поскольку он обеспечивает дублирование внешнего определения функции func1. Обратите внимание, что это func1, а не func3, что проблематично с точки C. Нет ничего формального в вашем func3. Ваш func2 также в порядке, если два определения никогда не "встречаются" друг с другом в одной и той же единицы перевода.

Одна возможная причина, по которой вы можете получить другой диагностический отчет от вашего компилятора, заключается в том, что ваш компилятор C может поддерживать функции inline в некотором нестандартном компиляторе (либо компилятор pre-C99, либо современный компилятор в нестандартном режиме "устаревший" ).

Честно говоря, мне трудно поверить, что вы получаете сообщение об ошибке func3 из любого компилятора, предполагая, что код, который вы опубликовали, точно отражает то, что вы пытаетесь скомпилировать. Скорее всего, вы опубликовали не настоящий код.

Ответ 5

Ошибка компиляции, которую вы видите, на самом деле является ошибкой компоновщика.

gcc и g++ обрабатывают static inline несколько иначе. inline была первой частью С++, а затем преобразована в расширение для многих компиляторов C, а затем добавлена ​​к стандарту C. Стандартная семантика может быть разной, но это могут быть просто разные реализации.

Он также может иметь какое-то отношение к некоторым сумасшедшим вещам, которые происходят с кодом на С++, который избавляется от дублирования материала шаблона, а также других дубликатов.

Ответ 6

в основном Inline - это поздняя запись в GCC (я имею в виду c-компилятор).    "[...] Встроенное определение не предоставляет внешнего определения функции и не запрещает внешнее определение в другой единицы перевода. Встроенное определение предоставляет альтернативу внешнему определению, которое переводчик может использовать для реализации любого вызов функции в одной и той же единицы перевода. Не указано, используется ли вызов функции с помощью встроенного определения или внешнего определения".   - ISO 9899: 1999 (E), стандарт C99, раздел 6.7.4