vim
следует набрать предлагаемый текст программы:
$ vim simple.c
Замечание 1. В языке Си для введения многострочных комментариев используется пара символов /*
и */
. Все что заключено между ними является комментарием и в машинный код программы по-умолчанию не включается, так что этот текст используется только программистом и не обязателен для написания.
С учётом этого замечания очевидно, что текст программы состоит всего из одной строки, в которой вызывается главная функция программы main()
, имеющая тип void
(т.е. ничего не возвращающая операционной системе). Список входных параметров функции тоже пуст. Кроме того, тело главной функции тоже пустое, т.к. блок {}
не содержит никаких операторов. Означает ли это, что написанное нами приложение вообще делать не будет? Для ответа на этот вопрос оттранслируем программу с помощью gcc
:
$ gcc simple.c
После этого убедимся в том, что в текущем каталоге появился файл с именем a.out
.
$ ls -l
Этот файл и есть наша программа. Легко можно убедиться, что она далеко не пуста, а значит какие-то действия выполняет. Давайте запустим её следующим образом:
$ ./a.out
Как и следовало ожидать, программа запустилась и завершилась, не сделав ничего полезного с точки зрения пользователя. Однако с точки зрения системного программиста этот процесс был гораздо более сложным. Сейчас мы попытаемся оценить насколько.
Для начала разберемся с тем, чем же отличаются друг от друга файлы simple.c
и a.out
. В этом нам поможет команда file
. Узнайте её назначение:
$ man file
и запустите её дважды так:
$ file simple.c
$ file a.out
Теперь узнайте назначение команды more
:
$ man more
После этого попытаемся изучить наши файлы с её помощью:
$ more simple.c
$ more a.out
Почему последняя введённая нами команда отработала именно так? Очевидно, что для изучения машинных кодов требуются другие средства. Такими средствами являются программы hexdump
и objdump
:
$ man hexdump
$ man objdump
Теперь с помощью этих инструментов постарайтесь исследовать вашу программу:
$ hexdump -C simple.c | more
$ hexdump -C a.out | more
$ objdump -s simple.c | more
$ objdump -s a.out | more
$ objdump -d a.out | more
Постарайтесь объяснить то, что вы при этом видите на экране.
Напишите текст следующей программы:
$ vim hello.c
Теперь изучите какие опции поддерживает транслятор gcc
и его версию:
$ gcc --help | more
$ gcc -v
После этого оттранслируйте и запустите вашу программу:
$ gcc hello.c -o hello
$ ls -l
$ ./hello
Объясните, что произошло? Для чего использована опция -o
?
Теперь прервём трансляцию после препроцессора:
$ gcc -E hello.c -o hello.1
$ ls -l
$ file hello.1
$ more hello.1
Что содержит файл hello.1
? Зачем нужен препроцессор?
Далее остановим трансляцию после компиляции программы:
$ gcc -S hello.c -o hello.2
$ ls -l
$ file hello.2
$ more hello.2
Что представляет собой файл hello.2
? Является ли этот файл программой и почему?
Теперь остановим трансляцию после ассемблирования:
$ gcc -c hello.c -o hello.3
$ ls -l
$ file hello.3
$ more hello.3
$ hexdump -C hello.3 | more
$ objdump -s hello.3 | more
$ objdump -d hello.3 | more
Что представляет собой файл hello.3
? Можно ли считать, что это готовая программа? Зачем нужна компоновка программы и имеет ли смысл останавливать трансляцию после компоновки?