Страницы

Поиск по вопросам

пятница, 31 января 2020 г.

Подробности работы компилятора и линкера [дубликат]

#cpp #компоновщик


        
             
                
                    
                        
                            На этот вопрос уже даны ответы здесь:
                            
                        
                    
                
                        
                            В каком случае нужно определять функцию в заголовочном
файле?
                                
                                    (5 ответов)
                                
                        
                        
                            Что такое “Правило одного определения” (One definition
rule)?
                                
                                    (1 ответ)
                                
                        
                                Закрыт 2 года назад.
            
                    
Рассмотрим следующий пример:

// file header1.h
#include 

void foo()
{
    std::cout << "Foo" << '\n';
}

//  file source1.cpp
#include "header1.h"


//  file main.cpp
#include "header1.h"

int main()
{
    return 0;
}


В связи с тем, что определение foo находится в хедере (что не очень хорошо), который
включён в более чем один .cpp файл, линкер честно выдает ошибку наподобие:


multiple definition of foo ... first defined in main.o ...



Одно из решений - сделать foo статической функцией, что обеспечит одно определение
в объектных файлах:

static foo() { ... }


второе: 

inline foo() { ... }


третье: 

static inline foo() { ... }


Вопросы:


Почему inline функция тоже решает эту проблему?
Какие принципиальные отличия у этих решений? 

    


Ответы

Ответ 1



inline решает эту проблему потому, что спецификация языка этого требует. Правило Одного Определения (One Definition Rule) для inline функций ясно говорит, что inline функция с внешним связыванием может определяться в программе много раз в разных единицах трансляции (точнее: должна определяться везде, где одна используется и везде - одинаково). А уж как компиляторы/линкеры реализуют это требование - это их проблемы. Фактически, если такая функция породила тело в нескольких объектных файлах (т.е. не все ее вызовы заинлайнились), то линкеру будет предписано не ругаться на тему "multiple definition", а молча выбрать одно-единственное тело и остальные выбросить. Так как "классический" формат объектного файла не позволяет определить, где начинается и где заканчивается тело конкретной функции, для целей возможного будущего удаления компилятор их обычно помещает в выделенные секции объектного файла. Принципиальное отличие между inline и static вариантом заключается в том, что inline функция сохраняет внешнее связывание (external linkage) и ведет себя во всех отношениях как функция со внешним связыванием. Например, если разные единицы трансляции попробуют взять адрес inline функции, то везде обязательно должно получиться одно и то же значение. Статические переменные, определенные внутри такой inline функции тоже существуют в единственном экземпляре во всей программе, как и в любой другой функции со внешним связыванием. В static варианте наоборот, функция получает внутреннее связывание (internal linkage) - каждая единица трансляции получает свою копию функции со своим уникальным адресом. Принципиальных отличий между static inline и static нет, с той только разницей, что в первом случае дается подсказка компилятору.

Ответ 2



это очевидно из самой сути inline -- если код такой функции в самом деле "заинлайнен", то на него существует (логически) только одна ссылка (а именно в том месте, где код "инлайнится"), следовательно никаких проблем с "multiple definition" (т.е. проблемы выбора той или иной точки входа) не существует. тут принципиальное отличие возникает при наличии static переменных у таких (inline и static inline функций. В каждой единице компиляции у static inline будут свои экземпляры таких переменных (а у "не static" один экземпляр static переменной на весь загрузочный модуль (результат линковки)).

Комментариев нет:

Отправить комментарий