Сначала вы должны понять встроенную модель C99 - возможно, что-то не так с вашими заголовками. Существует два вида определений для встроенных функций с внешней (нестатической) связью
Внешнее определение
Это определение функции может появиться только один раз во всей программе, в назначенном TU. Он предоставляет экспортированную функцию, которую можно использовать из других TU.
Встроенное определение
Они появляются в каждом TU, где объявлены как отдельное определение. Определения не должны быть идентичны друг другу или внешнему определению. При использовании внутреннего в библиотеке они могут не проверять аргументы функции, которые в противном случае выполнялись бы во внешнем определении.
Каждое определение функции имеет свои собственные локальные статические переменные , потому что их локальные объявления не имеют связи (они не разделяются, как в C ++). Определение нестатической встроенной функции будет встроенным определением, если
- Каждое объявление функции в TU включает спецификатор
inline
и
- Нет объявления функции в TU, включая спецификатор
extern
.
В противном случае определение, которое должно появиться в этом TU (потому что встроенные функции должны быть определены в том же TU, где объявлено), является внешним определением. При вызове встроенной функции не указано, используется ли внешнее или встроенное определение . Однако, поскольку функция, определенная во всех случаях, остается одинаковой (поскольку она имеет внешнюю связь), ее адрес сравнивается одинаково во всех случаях, независимо от того, сколько появляется встроенных определений. Поэтому, если вы берете адрес функции, скорее всего, компилятор преобразуется во внешнее определение (особенно если оптимизации отключены).
Пример, демонстрирующий неправильное использование inline
, поскольку он включает внешнее определение функции дважды в двух TU, что вызывает ошибку множественного определения
// included into two TUs
void f(void); // no inline specifier
inline void f(void) { }
Следующая программа опасна, поскольку компилятор может свободно использовать внешнее определение, но программа не предоставляет его
// main.c, only TU of the program
inline void g(void) {
printf("inline definition\n");
}
int main(void) {
g(); // could use external definition!
}
Я сделал несколько тестовых случаев с использованием GCC, которые дополнительно демонстрируют механизм:
main.c
#include <stdio.h>
inline void f(void);
// inline definition of 'f'
inline void f(void) {
printf("inline def main.c\n");
}
// defined in TU of second inline definition
void g(void);
// defined in TU of external definition
void h(void);
int main(void) {
// unspecified whether external definition is used!
f();
g();
h();
// will probably use external definition. But since we won't compare
// the address taken, the compiler can still use the inline definition.
// To prevent it, i tried and succeeded using "volatile".
void (*volatile fp)() = &f;
fp();
return 0;
}
main1.c
#include <stdio.h>
inline void f(void);
// inline definition of 'f'
inline void f(void) {
printf("inline def main1.c\n");
}
void g(void) {
f();
}
main2.c
#include <stdio.h>
// external definition!
extern inline void f(void);
inline void f(void) {
printf("external def\n");
}
void h(void) {
f(); // calls external def
}
Теперь программа выводит то, что мы ожидали!
$ gcc -std=c99 -O2 main.c main1.c main2.c
inline def main.c
inline def main1.c
external def
external def
Глядя на таблицу символов, мы увидим, что символ встроенного определения не экспортируется (из main1.o
), а внешнее определение экспортируется (из main2.o
).
Теперь, если каждая из ваших статических библиотек имеет внешнее определение своих встроенных функций (как и должно быть), они естественным образом конфликтуют друг с другом. Решение состоит в том, чтобы сделать встроенные функции статическими или просто переименовать их. Они всегда предоставляют внешние определения (поэтому они являются полноценными определениями), но они не экспортируются, поскольку имеют внутреннюю связь, поэтому не конфликтуют
static inline void f(void) {
printf("i'm unique in every TU\n");
}