Многократная ошибка компоновщика с использованием встроенных функций

Компоновщик сообщает о множественно определенных ошибках для встроенной функции.

У меня есть следующий код в заголовочном файле:

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 файлах, которые вызывают функцию.

Другие вопросы по тегам