Что такое единица перевода в C
Обычно используемое определение единицы перевода - это то, что приходит после предварительной обработки (включения заголовочных файлов, макросы и т.д. вместе с исходным файлом). Это определение достаточно понятно и стандарт C, 5.1.1.1, C11, говорит:
Программа C не обязательно должна быть переведена одновременно. Текст программы хранится в единицах, называемых исходными файлами (или файлы предварительной обработки) в этом Международном стандарте. Исходный файл вместе со всеми заголовками и исходными файлами, включенными в директиву предварительной обработки #include
, известен как блок перевода для предварительной обработки. После предварительной обработки блок перевода с предварительной обработкой называется единицей трансляции.
Более внимательно прочитайте первое предложение:
Программа C не обязательно должна быть переведена одновременно.
что подразумевает (для моего чтения), программа C может быть переведена в одно и то же время, не обязательно разбивая их на несколько исходных файлов предварительной обработки.
Также в конце того же абзаца в стандарте говорится:
Единицы перевода могут быть переведены отдельно и затем связаны с производством исполняемой программы.
который может (и обычно) интерпретироваться как компиляция отдельных объектных файлов, а затем, наконец, связывать их для создания одной исполняемой программы. Однако, если можно сделать вопрос из приведенного выше утверждения и спросить: означает ли это, что реализация может рассматривать несколько исходных файлов как единую единицу перевода, особенно для вызова типа:
gcc file1.c file2.c -o out
где компилятор имеет доступ ко всему источнику?
В частности, если реализация рассматривает file1.c
+ file2.c
(выше) как единую единицу перевода, можно ли считать ее несоответствующей?
Ответы
Ответ 1
Во второй строке вы указали:
Текст программы хранится в единицах, называемых исходными файлами (или файлы предварительной обработки) в этом Международном стандарте
Если есть два исходных файла, тогда есть два файла предварительной обработки и, следовательно, две единицы перевода для предварительной обработки и, следовательно, две единицы перевода. Один из них соответствует каждому исходному файлу.
Стандарт не определяет исходный файл. Я думаю, компилятор мог сказать: "Я составляю свою собственную версию" исходного файла ", объявляя, что file1.c
и file2.c
не являются исходными файлами в конце концов!" и объединить их, но это будет противоречить ожиданиям программистов. Я думаю, вам будет трудно сказать, что file1.c
не является исходным файлом.
Ответ 2
Однако, если можно задать вопрос из вышеприведенного утверждения и спросить: означает ли это, что реализация может рассматривать несколько исходных файлов как единый блок перевода
Нет. Определение ясное:
Исходный файл вместе со всеми заголовками и исходными файлами, включенными в директиву предварительной обработки #include, известен как единица перевода для предварительной обработки. После предварительной обработки блок перевода с предварительной обработкой называется единицей трансляции.
Блок перевода является результатом предварительного файла один и включает его. Тот факт, что вы можете перевести две единицы перевода одновременно, не означает, что вы можете рассматривать их как одну единицу перевода.
Ответ 3
Компиляторы могут свободно переводить несколько исходных файлов одновременно, но они не могут изменить свою семантику.
Транслирование нескольких файлов, вероятно, будет несколько быстрее (потому что компилятор запускается только один раз) и позволит улучшить оптимизацию всей программы: исходный код вызываемых функций в других единицах перевода затем доступен в точке вызова из других единиц перевода. Компилятор может проверять вызываемый код и использовать информацию, насколько это возможно, с помощью одной единицы перевода. Из руководства gcc 6.3.0:
Компилятор выполняет оптимизацию на основе знаний, которые он имеет в программе. Одновременная компиляция нескольких файлов в один выходной файл позволяет компилятору использовать информацию, полученную из всех файлов при компиляции каждого из них.
Вызываемые функции могут быть проверены на предмет отсутствия псевдонимов, фактической консистенции объектов с указателем и т.д., Что позволяет компилятору выполнять оптимизации, которые были бы неправильными в общем случае.
И, конечно, такие функции могут быть встроены.
Но есть семантика единиц (препроцессорной) трансляции (которые соответствуют исходным файлам после предварительной обработки, по вашей стандартной котировке), которые компилятор должен соблюдать. @Malcolm упомянул одну, файло-статическую переменную. Я чувствую, что могут возникнуть другие, более тонкие проблемы, связанные с декларациями и порядком декларации.
Еще одна очевидная проблема с кодом исходного кода касается определений. Из проекта n1570, 6.10.3.5:
Определение макроса длится (независимо от структуры блока) до тех пор, пока не будет встречена соответствующая директива #undef или (если она не встречается) до конца блока перевода предварительной обработки.
Обе проблемы запрещают простую конкатенацию исходного файла C; компилятор должен дополнительно применять некоторую рудиментарную логику.
Ответ 4
Блок перевода означает файл точки C. Во всех смыслах и целях, включая связанную с ним точку h, входит. Редко директивы #include используются для добавления других типов файлов или других файлов точек C.
Статические переменные видны только внутри единицы перевода. Очень часто приходится иметь несколько публичных функций с внешней связью и многими статическими функциями и элементами данных t. Таким образом, C-единица перевода немного похожа на одноэлементный С++-класс. Если компилятор не обрабатывает статические данные правильно, он не соответствует требованиям.
Обычно для каждой единицы перевода создается один объектный файл, и они затем связываются компоновщиком. Это на самом деле не предусмотрено стандартом, но является естественным и очевидным способом делать что-то в среде, где файлы дешевы для создания и компиляции относительно медленно.