Многократная ошибка компоновщика с использованием встроенных функций
Компоновщик сообщает о множественно определенных ошибках для встроенной функции.
У меня есть следующий код в заголовочном файле:
struct Port_Pin
{
volatile uint32_t * port_addr_set_value; //!< Writing the pin value here sets the pin to high.
volatile uint32_t * port_addr_clr_value; //!< Writing the pin value to this port clears the pin to low.
volatile uint32_t * port_addr_read_value; //!< Address to read pin value.
volatile uint32_t * port_addr_enable; //!< Writing the pin value here enables the pin (for reading or writing).
volatile uint32_t * port_addr_disable; //!< Writing the pin value here disables the pin.
volatile uint32_t * port_addr_dir_output; //!< Writing the pin value here sets the pin as an output.
volatile uint32_t * port_addr_dir_input; //!< Writing the pin value here sets the pin as an input.
unsigned int pin_bit_position; //!< Zero based, where position zero is first bit position.
};
inline void
Write_Port_Pin(const struct Port_Pin * p_port,
uint8_t bit)
{
volatile uint32_t * port_addr = 0;
port_addr = ((bit & 1) == 0) ? p_port->port_addr_clr_value
: p_port->port_addr_set_value;
*port_addr = 1 << p_port->pin_bit_position;
return;
}
Я включаю заголовочный файл в несколько исходных файлов (.c).
Я хотел бы, чтобы вышеуказанная функция была вставлена в строку, где бы она ни вызывалась.
Есть ли способ для этого без нескольких определений функции в каждом исходном файле, который включен? Если да, приведите пример.
Мне нужна оптимизация производительности для встроенной платформы.
Являются ли компиляторы или компоновщики достаточно умными, чтобы встроить функции, когда они определены в других единицах перевода?
Я использую компилятор Green Hills, 4.2.4 на встроенной платформе ARM9. Предположим, до 2000 C языковой стандарт. Это код C, а не C++.
5 ответов
inline - это просто предложение, а не команда. Однако в целом компиляторы достаточно умны, чтобы делать правильные вещи (и Green Hills имеет хорошую репутацию в плане оптимизации).
Сделайте функцию static inline, которая не позволит компилятору сделать символ экспортируемым. Это должно исправить ваши ошибки в ссылках с несколькими определениями... компоновщик жалуется, что одна и та же функция экспортируется из нескольких исходных модулей.
Некоторые важные замечания:
Похоже, вы не правильно охраняли свой заголовок.
#ifndef NAME_H
#define NAME_H
//...contents go here...
#endif // NAME_H
Это предотвращает множественные определения, когда заголовок #include
буду не раз.
Также кажется, что вы думаете, что можете заставить компилятор встроить свою функцию. Это не правильно. Помимо сумасшедшего и неясного флага компилятора, компилятор всегда решает, хочет ли он встроить вашу функцию в производимый код. Встроенное ключевое слово имеет другое значение / цель, чем вы думаете, см. Здесь
Непонятно, что вы имеете в виду, почему "спецификация языка до 2000 года" - последний стандарт был завершен в 1999 году. До этого inline
не было ключевым словом вообще.
Стандарт 1999 года имеет следующее:
Если все объявления области действия файла для функции в модуле перевода включают
inline
спецификатор функции безextern
тогда определение в этой единице перевода является встроенным определением. Встроенное определение не предоставляет внешнего определения для функции и не запрещает внешнее определение в другой единице перевода. Встроенное определение предоставляет альтернативу внешнему определению, которое переводчик может использовать для реализации любого вызова функции в том же модуле перевода. Не определено, использует ли вызов функции встроенное определение или внешнее определение.
Это означает, что до тех пор, пока у вас нет декларации Write_Port_Pin()
с extern
квалификатор, компилятор не должен генерировать внешнее определение функции, поэтому он не должен беспокоить компоновщик. Я бы отправил это как ошибку вашему поставщику компилятора на вашем месте.
Если у вас есть встроенное определение в файле.h, и вы включаете его во многие файлы.c и попробуйте скомпилировать библиотеку с помощью компилятора armcc. Теперь, если вы используете опцию --gnu compiler для компиляции кода armcc, тогда вы также увидите, что во время компоновки вы увидите ошибку multiply define, потому что тогда компилятор помещает определение в каждый файл.c и экспортирует его. Кажется, при попытке сделать ваш код GCC-совместимым, мы получаем этот недостаток.
Чтобы избежать этого, можно использовать параметр --c99 вместо --gnu.
и избавиться от этой проблемы множественного определения в файлах.c из-за экспорта встроенных функций компилятором.
В C вы не можете определить функцию с одинаковым именем в нескольких местах, независимо от того, встроена она или нет.
Лучший способ справиться с этим - объявить функцию в заголовке (вместе с определением структуры, от которого она зависит, например:
/* port_control.h */
struct Port_Pin
{
volatile uint32_t * port_addr_set_value; //!< Writing the pin value here sets the pin to high.
volatile uint32_t * port_addr_clr_value; //!< Writing the pin value to this port clears the pin to low.
volatile uint32_t * port_addr_read_value; //!< Address to read pin value.
volatile uint32_t * port_addr_enable; //!< Writing the pin value here enables the pin (for reading or writing).
volatile uint32_t * port_addr_disable; //!< Writing the pin value here disables the pin.
volatile uint32_t * port_addr_dir_output; //!< Writing the pin value here sets the pin as an output.
volatile uint32_t * port_addr_dir_input; //!< Writing the pin value here sets the pin as an input.
unsigned int pin_bit_position; //!< Zero based, where position zero is first bit position.
};
/* Declare the function here so other modules know about it. */
inline void
Write_Port_Pin(const struct Port_Pin * p_port,
uint8_t bit);
Затем определите функцию в исходном файле.c в одном месте:
/* port_control.c */
#include "port_control.h"
inline void
Write_Port_Pin(const struct Port_Pin * p_port,
uint8_t bit)
{
volatile uint32_t * port_addr = 0;
port_addr = ((bit & 1) == 0) ? p_port->port_addr_clr_value
: p_port->port_addr_set_value;
*port_addr = 1 << p_port->pin_bit_position;
return;
}
Затем #include этот заголовочный файл во всех.c файлах, которые вызывают функцию.