Особенности реализации функций ввода-вывода
В приведенном языке для ввода и вывода данных определены две «встроенные» функции print
и input
, реализация которых не может быть переопределена пользователем. Тем не менее на самом деле обе они не имеют под собой реализации на этом языке, и в местах их вызова происходит обращение к стандартной библиотеке языка Си.
Более того, в силу специфики использования функции print
и input
можно считать в большей степени языковыми конструкциями, нежели функциями. Функция print
может принимать одно выражение любого из поддерживаемых типов в качестве аргумента (в языке C++, например, такое поведение функций называется перегрузкой), что для пользовательских функций запрещено. Функция input
ведет себя еще более нестандартно: она может находиться только в операциях присваивания и только в качестве правого операнда, при этом не принимает аргументов, но тип возвращаемого значения при ее вызове определяется типом переменной в роли левого операнда того же выражения (фигурально такое поведение можно назвать «перегрузкой по типу возвращаемого значения», по аналогии с обычными перегрузками из C++).
Рассмотрим теперь, каким образом вызовы этих функций компилируются во время генерации кода LLVM IR.
В месте вызова функции print
происходит вставка инструкции LLVM IR, декларирующей вызов функции printf
. Как известно, эта функция принимает форматирующую строку и список выражений, тип которых должен совпадать с типами, соответствующим шаблонным последовательностям в форматирующей строке. Таким образом, при генерации кода инструкция вызова printf
содержит два передаваемых аргумента:
форматирующую строку, состоящую из одной шаблонной последовательности (типу
int
соответствует%d
, типуfloat
соответствует%lf
, типуstr
соответствует%s
, остальные типы содержат фиксированное множество значений и свою строку для каждого из значений:True
,False
,None
);значение выражения определенного типа, которое должно быть выведено в стандартный поток вывода (stdout).
В месте вызова функции input
происходит аналогичная вставка вызова функции scanf
. Принцип использования этой функции отличается тем, что вместо значения выражения должен передаваться адрес переменной, в которую будет записано значение, полученное из стандартного потока ввода (stdin).