множественное определение встроенной функции - PullRequest
25 голосов
/ 27 апреля 2010

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

У меня есть заголовочный файл 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, без использования каких-либо заголовочных файлов C ++ и т. Д.). Отображаемая ошибка: «Дубликат определения встроенной функции - func3».

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

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

500
6
700

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

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

Ответы [ 6 ]

13 голосов
/ 24 декабря 2012

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

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

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

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

  1. Переименовать tran.cpp в tran.c
  2. Компилировать с 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)

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

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

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

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

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

Почему он компилируется с использованием g++.

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

3 голосов
/ 17 ноября 2011

Ошибка компиляции из-за дублирующего определения func1 ();

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

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

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

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

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

2 голосов
/ 27 апреля 2010

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

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

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

5
6
7

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

5
6
700

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

1 голос
/ 23 декабря 2012

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

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

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

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

0 голосов
/ 23 декабря 2012

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

0 голосов
/ 27 апреля 2010

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

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

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

...