Некоторые общие вопросы C - PullRequest
6 голосов
/ 03 апреля 2010

Я пытаюсь полностью понять процесс написания кода на некотором языке для выполнения ОС. В моем случае язык будет C, а ОС - Windows. Пока что я прочитал много разных статей, но я не уверен, правильно ли я понимаю этот процесс, и я хотел бы спросить вас, знаете ли вы хорошие статьи по некоторым предметам, которые я не смог найти.

Итак, что я думаю, что знаю о C (и в основном других языках):

Сам компилятор C обрабатывает только типы данных, основные математические операции, операции с указателями и работу с функциями. Под работой с функциями я подразумеваю, как передать ему аргумент и как получить вывод из функции. Во время компиляции вызов функции заменяется передачей аргументов в стек, и, если функция не является встроенной, ее вызов заменяется некоторым символом для компоновщика. Связать, чем найти определение функции, и заменить символ, чтобы перейти к адресу этой функции (и, конечно, чем перейти назад к программе).

Если вышеизложенное в целом верно и я правильно понял, где в финальный файл .exe фактически компоновщик сохраняет функции? После функции main ()? А что создает заголовок .exe? Компилятор или компоновщик?

Теперь дополнительными возможностями C, известными сегодня как стандартная библиотека C, является набор функций и их объявлений, которые другие программисты написали для расширения и упрощения использования языка C. Но такие функции, как printf () были (или могли быть?) Написаны на другом языке или ассемблере. И возникает следующий вопрос: можно ли, например, напечатать функцию printf () на чистом C без использования ассемблера?

Я знаю, что это довольно большой вопрос, но я просто хочу знать, прав я или нет. И поверьте мне, я прочитал много статей в Интернете и не стал бы спрашивать вас, могу ли я найти эти сведения вместе в одном месте, в одной статье. Я должен по частям собирать информацию, поэтому не уверен, прав ли я. Спасибо.

Ответы [ 3 ]

6 голосов
/ 03 апреля 2010

Я думаю, что вы знакомы с некоторой информацией, которая менее актуальна для начинающего программиста на Си и которая может сбить вас с толку - часть цели использования языка более высокого уровня, подобного этому, состоит в том, чтобы не задумываться о том, как этот процесс работает. Однако со временем важно понять процесс. Я думаю, что вы, как правило, правильно понимаете это.

Компилятор C просто берет код C и генерирует объектные файлы, содержащие машинный язык. Большая часть объектного файла занята содержимым функций. Например, простой вызов функции в C будет представлен в скомпилированной форме как операторы низкого уровня, чтобы помещать вещи в стек, изменять указатель инструкций и т. Д.

Библиотека C и любые другие используемые вами библиотеки уже доступны в этой скомпилированной форме.

Компоновщик - это то, что объединяет все соответствующие объектные файлы, разрешает все зависимости (например, один объектный файл, вызывающий функцию в стандартной библиотеке), а затем создает исполняемый файл.

Что касается языковых библиотек, написанных на: Думайте о каждой функции как о чёрном ящике. Пока черный ящик имеет стандартный интерфейс (соглашение о вызовах C; то есть он принимает аргументы определенным образом, возвращает значения определенным образом и т. Д.), Как он написан внутри, не имеет значения. Чаще всего функции будут написаны на C или непосредственно в сборке. К тому времени, когда они превращают его в объектный файл (или как скомпилированную библиотеку), уже не имеет значения, как они были изначально созданы, важно то, что они теперь находятся в форме скомпилированного компьютера.

Формат исполняемого файла зависит от операционной системы, но большая часть тела исполняемого файла в Windows очень похожа на объектные файлы. Представьте, что кто-то объединил все объектные файлы, а затем добавил немного клея. Клей загружает связанные вещи, а затем вызывает main (). Например, когда я был ребенком, люди получали удовольствие от «изменения клея», чтобы добавить еще одну функцию перед main (), которая отображала бы заставку с их именем.

Следует отметить, что независимо от того, какой язык вы используете, в конечном итоге вам придется использовать службы операционной системы. Например, для отображения содержимого на экране, для управления процессами и т. Д. Большинство операционных систем имеют API, который также можно вызывать аналогичным образом, но его содержимое не включается в ваш EXE-файл. Например, когда вы запускаете браузер, это исполняемый файл, но в какой-то момент происходит обращение к API Windows для создания окна или загрузки шрифта. Если бы это было частью вашего EXE, ваш EXE был бы огромным. Так что даже в вашем исполняемом файле есть «недостающие ссылки». Обычно они решаются во время загрузки или во время выполнения, в зависимости от операционной системы.

1 голос
/ 04 апреля 2010

Я новый пользователь, и эта система не позволяет мне публиковать более одной ссылки. Чтобы обойти это ограничение, я разместил некоторую идею в своем блоге http://zhinkaas.blogspot.com/2010/04/how-does-c-program-work.html. Мне потребовалось некоторое время, чтобы получить все ссылки, но в целом они должны помочь вам начать.

0 голосов
/ 03 апреля 2010

Компилятор отвечает за перевод всех ваших функций, написанных на C, в сборку, которую он сохраняет в объектном файле (например, DLL или EXE). Таким образом, если вы напишите файл .c, который имеет основную функцию и несколько других функций, компилятор преобразует их все в сборку и сохраняет их вместе в файле EXE. Затем, когда вы запускаете файл, загрузчик (который является частью ОС) знает, что нужно сначала запустить основную функцию. В противном случае основная функция аналогична любой другой функции для компилятора.

Компоновщик отвечает за разрешение любых ссылок между функциями и переменными в одном объектном файле со ссылками в других файлах. Например, если вы вызываете printf (), так как вы не определяете функцию printf () самостоятельно, компоновщик отвечает за то, чтобы убедиться, что вызов printf () направлен в нужную системную библиотеку, в которой определена printf (). Это делается во время компиляции.

printf () действительно записывается на чистом языке C. Что он делает, так это вызывает системный вызов в ОС, который знает, как на самом деле отправлять символы на стандартный вывод (например, оконный терминал). Когда вы вызываете printf () в своей программе, во время компиляции компоновщик отвечает за связывание вашего вызова с функцией printf () в стандартных библиотеках C. Когда функция передается во время выполнения, printf () форматирует аргументы должным образом, а затем вызывает соответствующий системный вызов ОС для фактического отображения символов.

...