Ответ 1
В чем разница между библиотечной функцией и встроенной функцией?
Встроенная функция - это то, что компилятор имеет некоторые знания непосредственно внутри самого компилятора. Библиотечная функция просто определена в библиотеке. Встроенная функция и функция библиотеки с тем же именем могут существовать, поэтому для остальных ваших вопросов я буду рассматривать "библиотечную функцию" как "библиотечную функцию, которая не является встроенной функцией".
Есть ли что-то, что встроенная функция может выполнять, но функция библиотеки не может?
Да. Встроенная функция может выбирать, например, не для оценки своих аргументов:
int main() {
int i = 0;
__builtin_constant_p (++i); // checks whether ++i is a constant expression
// does not evaluate ++i
return i; // returns 0
}
Это связано с тем, что встроенная функция может быть преобразована компилятором во что-то еще, что фактически не должно содержать никакого вызова функции.
Могу ли я написать библиотечную функцию, которая выполняет ту же задачу, что и сборка функции printf?
Существует некоторое встроенное знание printf
, но по большей части это прекрасно выполнимо. Посмотрите, как использовать <stdarg.h>
.
Как я могу указать тип входных параметров (% f, float или double)?
Вы должны доверять вызывающему абоненту, чтобы строка формата соответствовала остальным аргументам; вы не можете обнаружить что-то вроде передачи int
, когда строка формата ожидает double
. Но вам не нужно обрабатывать разницу между float
и double
, потому что невозможно передать float
в printf
: он будет преобразован в double
(независимо от строки формата) до printf
видит это. Требования printf
были тщательно выполнены, чтобы избежать необходимости в какой-либо магии компилятора.
Машинные инструкции функций сборки GCC не хранятся в библиотеке, правильно?
Вызовы во встроенные функции преобразуются во время компиляции, но это преобразование может просто привести к вызову библиотечной функции с тем же именем.
Где они?
Если преобразование выполняется во время компиляции, машинных инструкций нет. Вызов преобразуется в другой код, и этот код затем скомпилируется для получения машинных инструкций. Если результатом является вызов функции библиотеки, машинные инструкции для этой библиотечной функции являются частью библиотеки.
Когда вы выполняете привязку, как вы можете контролировать, куда поместить эти коды функций сборки?
Я не понимаю, что вы имеете в виду здесь. Вызов встроенной функции преобразуется во время компиляции в другой код, а другой код затем компилируется как часть функции, содержащей вызов. Он будет помещен туда, где будет оставлен остальной код этой содержащей функции.
Почему иногда я могу отправлять сообщения об ошибках, например "undefined ссылка на __builtin_stdarg_start" при компоновке
Нет встроенной функции __builtin_stdarg_start
, несмотря на префикс __builtin
, поэтому это рассматривается как вызов функции библиотеки. И нет никакой библиотечной функции __builtin_stdarg_start
, поэтому компоновщик обнаруживает это как ошибку.
Раньше была встроенная функция __builtin_stdarg_start
, но она была удалена много лет назад, и код никогда не должен был ее использовать в первую очередь.
gcc -c main.c, nm показывает, что в main.o нет символа printf (только main (T) и puts (U)), почему?
Это потому, что printf
существует как встроенная функция, так и как функция библиотеки. Встроенная функция обычно просто вызывает библиотечную функцию, но иногда это возможно сделать лучше, в том числе и в вашем примере. В этом случае встроенная функция printf
может дать правильный результат без вызова библиотечной функции printf
.