глобальная встроенная функция между двумя файлами c

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

Я хочу объявить функцию inline в test.h, определить ее в main.c и вызвать из test.c.

main.c и test.c оба #include "test.h" (для примера кода нажмите на ссылку выше).

Это в основном какая-то функция обратного вызова, которую пользователь может включить/отключить. И только в одном файле функция должна быть определена.

Я знаю, что inline - это просто предложение для компилятора, и оно не имеет большого значения для современных процессоров, но это для 8-битного микроконтроллера, и это действительно нужно.

ИЗМЕНИТЬ:

У меня есть функция в test.c, которая вызывает эту встроенную функцию. Я просто хочу заменить вызов телом функции, определенной в main.c. Я надеюсь, что в этом есть смысл.


person user1806687    schedule 19.02.2015    source источник
comment
Обязательно, чтобы существовало только одно определение.   -  person Iharob Al Asimi    schedule 19.02.2015
comment
@lurker Разве это не потребует от меня включения main.c ? Что именно ты имеешь ввиду?   -  person user1806687    schedule 19.02.2015
comment
Об этом позаботится компоновщик, определение должно существовать только один раз, затем вы сообщаете компилятору, что является прототипом функции в другом c-файле, и на этапе компоновки компоновщик находит определение функции.   -  person Iharob Al Asimi    schedule 19.02.2015


Ответы (4)


Это работает точно так же, у вас будет только одно определение функции, скажем, это функция int add(int x, int y);, затем вы определяете ее в main.c

main.c:

int add(int x, int y)
{
    return x + y;
}

тогда, если вы хотите использовать его в другом файле c, все, что вам нужно, это это

test.c

int add(int x, int y); /* just a declaration, a function prototype */

int addWrapper(int x, int y)
{
    return add(x, y);
}

скомпилировать вот так

gcc -Wall -Werror main.c test.c -o some_output_file_name

и компоновщик позаботится о поиске определения функции.

Если функция не определена ни в одном из скомпилированных файлов, то

Undefined reference to `int add(int x, int y);' in ...

будет выдана ошибка.

Если вы хотите, чтобы компилятор вставил тело функции, то вставьте тело функции самостоятельно, используйте макрос препроцессора

header.h

#define add(x,y) ((x) + (y))

а потом

#include "header.h"

int addWrapper(int x, int y)
{
    return add(x, y);
}

заменит add(x, y) на x + y.

Если функция возвращает void, то хорошим трюком является использование этого

#define inlinedFunction(parameters) \
    do {                            \
        /* function body here */    \
    } while (0)

вы должны добавить обратную косую черту продолжения в конце каждой строки в определении макроса.

person Iharob Al Asimi    schedule 19.02.2015
comment
Но это не встраивает функцию, которую запрашивает OP, не так ли? Это просто стандартный вызов функции. - person lurker; 19.02.2015
comment
Использование макросов могло бы работать, но это просто уродливо. Вы уверены, что нет другого пути? Мне действительно нужно, чтобы пользователь (main.c) определил эту функцию, а не где-то в заголовке. - person user1806687; 19.02.2015
comment
@user1806687 user1806687 Если вы хотите убедиться, что код будет заменен, его нет, и почему это уродливо? вы будете иметь его в своем заголовочном файле и вызывать его, как если бы это была функция. - person Iharob Al Asimi; 19.02.2015
comment
Потому что пользователю придется сделать оператор #define вместо того, чтобы просто создать функцию. - person user1806687; 19.02.2015
comment
@ user1806687 Я не понимаю, зачем пользователю определение? вы предоставляете его в своем заголовочном файле. - person Iharob Al Asimi; 19.02.2015
comment
Встроенные функции и макросы — это совершенно разные вещи. Вы определяете встроенные функции с ключевым словом inline. Встроенные функции анализируются компилятором, но макросы анализируются препроцессором, а встроенные функции безопасны для типов, но не макросы. Так что это не отвечает на вопрос. - person Muhammed Kadir; 25.03.2019

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

person unwind    schedule 19.02.2015
comment
У меня есть функция в test.c, которая вызывает эту функцию inlined. Я просто хочу заменить вызов телом функции, определенной в main.c. Я надеюсь, что в этом есть смысл. - person user1806687; 19.02.2015
comment
@ user1806687 используйте макрос. - person Iharob Al Asimi; 19.02.2015

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

Per C99 Если и main.c, и test.c должны вызывать одну и ту же функцию И она должна быть помечена как inline, то я считаю, что она ДОЛЖНА быть определена в заголовке, потому что функция inline не может охватывать единицы перевода (т.е. файлы ".c").

Например:

test.h:

static inline int add(int a, int b) { return a + b; }

main.c:

#include "test.h"
void main(void)
{
   int x = add(10,15);
}

test.c:

#include "test.h"
int test(void)
{
   int x = add(10,15);
   return x;
}

EDIT: См. здесь лучшее объяснение использования C99 inline: Как объявить встроенную функцию в многофайловом проекте C99. ?

Однако это может привести к странному поведению результирующего объектного кода. Например, я видел, как компилятор из крупного MCU mfg (который намеренно останется неназванным) генерирует объектный код для inline функций #included в заданной единице перевода вместо их встраивания. Файл был включен в несколько мест и содержал множество функций, что привело к значительному увеличению общего использования ПЗУ во всей кодовой базе, поскольку компоновщик также был неспособен удалить мертвый код (см. --gc-sections для gcc) И не смог улучшить производительность, поскольку функции были на самом деле никогда не встраивался, хотя это было технически правильным использованием встроенного.

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

TL;DR: Если вы используете inline для микроконтроллеров, 1) обратитесь к руководству по компилятору и 2) следите за сгенерированным объектным кодом.

person Brian McFarland    schedule 19.02.2015
comment
@ user1806687 Я исправил (слегка) использование C99 inline. Смотрите обновленный ответ и ссылку для получения дополнительной информации. Однако предостережения о просмотре сгенерированного кода остаются в силе. - person Brian McFarland; 19.02.2015
comment
static inline должен убедиться, что для функции не генерируется символ и код. - person nwellnhof; 19.02.2015
comment
@nwellnhof - это не для рассматриваемого компилятора. Но для GCC он должен подойти. - person Brian McFarland; 19.02.2015
comment
@BrianMcFarland Итак, как мне использовать static inline в коде для достижения этого? - person user1806687; 19.02.2015
comment
См. обновленный пример. Я считаю, что это сделает то, что вам нужно, если я правильно понимаю вопрос. - person Brian McFarland; 19.02.2015
comment
@BrianMcFarland Я имею в виду, как бы я это использовал. Должен ли я использовать staic inline void some_func(void); в test.h и static inline void some_func(void) {} в main.c. Но это дает ошибки. - person user1806687; 19.02.2015
comment
Нет, удалите static inline void some_funct(void){} из main.c. Встроенные функции технически не могут существовать в нескольких единицах перевода. Помещение его в заголовок фактически выполняет копирование/вставку во все .c, которые включают заголовок, но вы по-прежнему поддерживаете только одну версию кода. - person Brian McFarland; 19.02.2015
comment
@BrianMcFarland Ааа, теперь это имеет смысл. Спасибо! - person user1806687; 19.02.2015

Вы можете использовать ключевое слово inline в C, но если вы используете компилятор GNU GCC, он также потребует, чтобы встроенные функции были функциями static. Таким образом, вы сможете использовать эти функции только в одном и том же исходном файле. Если вы хотите объявить глобальную встроенную функцию, которую вы определяете в каком-либо заголовочном файле, и использовать ее в исходных файлах, которые включают этот заголовочный файл, вы можете использовать атрибут always_inline GNU GCC следующим образом:

static inline __attribute__((always_inline)) int foo(int a)
{
  return a+2;
}

Не забудьте объявить тело встроенной функции в том же заголовочном файле.

person Muhammed Kadir    schedule 25.03.2019