group-telegram.com/cxx95/38
Last Update:
#compiler
Прикладные linkage types в C++ 🔗
Стандартное добавление функции в C++ происходит так: в .h
-файле пишется объявление функции (которое попадает в много translation unit), а в одном .cpp
-файле пишется определение функции.
Чтобы определить функцию в .h
-файле, чаще всего пишут спецификатор inline
(это рекомендуемый путь), в несколько раз реже static
.
Посмотрим, каким будет LLVM IR (компиляторное промежуточное представление C++-кода) для int sum1()
, static int sum2()
, inline int sum3()
: ссылка на godbolt.
У LLVM IR есть интересный список linkage types для символов, типов много. Linkage type определяет, как ведет себя символ во время линковки в бинарник. (Символ - это функция или глобальная переменная)
▪️ У sum1
дефолтный linkage type external
- возможно ровно 1 определение символа. Линковщик выдаст ошибку, если в программе будет 0 или >1 определения.
▪️ У sum2
linkage type internal
- символ доступен только из того translation unit, где определен. Если в процессе линковки попадется одноименный символ, то линковщик просто переименует sum2
, чтобы не было коллизии.
▪️ У sum3
linkage type linkonce_odr
. Это типичный weak symbol, как и некоторые другие типы (linkonce
, weak
).
В программе может быть несколько weak-определений одного и того же символа.
Если все определения символа являются weak, то линковщик берет рандомное определение.
Если какое-то из определений символа является strong (как у sum1
), то линковщик берет strong-определение.
В чем отличие linkonce
от linkonce_odr
?
▪️ В общем случае weak-определения могут быть разными, поэтому компилятор не имеет права заинлайнить вызов weak-функции (после линковки у функции может оказаться совсем другое определение)
▪️ Но Стандарт С++ требует от программиста обеспечить, чтобы inline-функции имели идентичное определение (это естественным образом достигается, если определение находится в .h
-файле)
▪️ Поэтому компилятор имеет право заинлайнить вызов метода sum3
- программа от этого сломаться не сможет
Скомпилируем объектный файл
> clang++ -c link.cppНа Linux получим объектный файл формата ELF. Используем утилиту
readelf
для чтения таблицы символов. Чтобы вывести исходные имена методов (не mangled-имена), используем утилиту c++filt
:> readelf -s link.o | c++filtПолучим такой вывод (опустил другие символы):
Symbol table '.symtab' contains 21 entries:В общем случае выгоднее использовать inline-методы, чем static-методы! Потому что так меньше загрязняется бинарник, и все статические переменные внутри функции живут в количестве 1 штуки, а не в N штук:
Num: Value Size Type Bind Vis Ndx Name
6: 00000000000000c0 18 FUNC LOCAL DEFAULT 2 sum2(int, int)
14: 0000000000000000 18 FUNC GLOBAL DEFAULT 2 sum1(int, int)
20: 0000000000000000 18 FUNC WEAK DEFAULT 7 sum3(int, int)
inline int* get_address() {
// `dummy` займет sizeof(int) памяти, а со static-методом было бы N*sizeof(int)
static int dummy;
// inline-метод - возвращает один и тот же указатель
// static-метод - уникальный для каждого translation unit
return &dummy;
}
BY C++95
Warning: Undefined variable $i in /var/www/group-telegram/post.php on line 260
Share with your friend now:
group-telegram.com/cxx95/38