Пожалуйста, кто-нибудь развейте мои сомнения в том, что во время процесса компиляции препроцессор заменяет объявление предопределенной функции, но не определение, тогда почему мы не получили ошибку во время компиляции, что функция, не определенная как определение функции, добавляется в код после компиляции на этапе компоновки??
Подскажите, пожалуйста, как на самом деле работает препроцессор и связывание?
Короче говоря и очень упрощенно, представьте себе препроцессор как программу, которая обрабатывает исходный файл перед реальным компилятором. Он выполняет замену копипастом включаемых файлов и макросов. Очень просто. Затем компилятор берет предварительно обработанный исходный код и компилирует его в объектный файл. Затем этот объектный файл передается компоновщику, который добавляет другие объектные файлы и библиотеки для создания окончательного исполняемого файла.
Из моего описания должно быть ясно (надеюсь), что препроцессор на самом деле ничего не знает о самом коде, о операторах, объявлениях или определениях. Поэтому, пожалуйста, отредактируйте свой вопрос, чтобы показать нам минимально воспроизводимый пример кода, который вас интересует, и дайте нам более подробную информацию.
Препроцессор выполняет в основном 3 задачи: 1. удаляет комментарии 2. расширяет макросы 3. включает содержимое заголовочных файлов, содержащих объявления библиотечных функций
если препроцессор включает содержимое файла заголовка, какой нужен компоновщику для связывания объектного кода файлов заголовков, если они уже присутствуют в коде
Кажется, сейчас самое время узнать о концепции единиц перевода.
Подводя итог, единица перевода (или сокращенно TU) представляет собой единый исходный файл со всеми включенными заголовочными файлами. Это то, что на самом деле видит компилятор, и он ничего не знает о других единицах перевода.
Таким образом, если есть объявление функции, компилятор будет знать об этом, но если определение (реализация) находится в другом TU, то компилятор об этом не узнает. Компилятор может сгенерировать вызов функции, но оставить в объектном файле своего рода примечание о том, что в другой единице перевода есть ссылка на функцию.
Здесь на помощь приходит компоновщик: он принимает все единицы перевода (объектные файлы) и библиотеки и разрешает ссылки на функции в различных единицах перевода.
Сами заголовочные файлы никогда не «компилируются» сами по себе, они всегда включаются в исходные файлы. По сути, код заголовочного файла копируется в исходный файл.
Таким образом, как только препроцессор завершен, ни компилятор, ни компоновщик ничего не знают о файлах заголовков. Все, что они видят, — это отдельные единицы перевода.
спасибо за ответ, это прояснило мои сомнения, поскольку я пытался использовать функцию printf без использования #include<stdio.h> и просто объявлял функцию printf() для себя, и я получил желаемый результат с предупреждением. Но как в этом случае компоновщик узнайте, какой объектный код файла он должен был включить, поскольку я не добавлял ни одного файла заголовка
@KRISHNAKANTMALI Как я уже сказал, компоновщик вообще не знает о файлах заголовков. Но функция printf
— это стандартная функция C, которая определена (реализована) в стандартной библиотеке C, связанной по умолчанию. Поэтому компоновщик найдет определение.
@KRISHNAKANTMALI Также обратите внимание, что существует только одна стандартная библиотека C, но у нее много заголовочных файлов. Но может быть и наоборот, когда у вас есть один (или несколько) заголовочных файлов, но нет библиотеки, весь необходимый код находится в заголовочном файле. Таким образом, на самом деле нет никакой связи между файлами заголовков и библиотеками.
что ты имеешь в виду? Препроцессор ничего не знает об объявлении или определении функции.