Ответ 1
Сначала вам нужно понять встроенную модель C99 - возможно, что-то не так с вашими заголовками. Существуют два типа определений для встроенных функций с внешней (нестатической) привязкой
-
Внешнее определение
Это определение функции может появляться только один раз во всей программе в обозначенном ТУ. Он предоставляет экспортированную функцию, которая может использоваться из других ТУ. -
Встроенное определение
Они появляются в каждом TU, который объявляется как отдельное определение. Определения не обязательно должны быть идентичны друг другу или внешнему определению. Если они используются внутри библиотеки, они могут опустить проверку аргументов функции, которые в противном случае были бы выполнены во внешнем определении.
Каждое определение функции имеет свои собственные локальные статические переменные, потому что их локальные объявления не имеют связи (они не разделяются, как в С++). Определение нестатической встроенной функции будет встроенным определением, если
- Каждое объявление функции в 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");
}