Войти
ПрограммированиеСтатьиОбщее

Основы программирования игр для Unix-подобных систем. Часть I

Внимание! Этот документ ещё не опубликован.

Автор:

Предисловие

Данная статья предназначена для программистов игр, которые желают разобраться с основами программирования игр в Unix-подобных операционных системах (в первую очередь систем семейства GNU/Linux/X11, на одной из которых и будут приведены примеры). Так что вам необходим компьютер с Unix или Unix-подобной операционной системой. Вам необходимо убедится, что у вас установлено:

Если вы вдруг встречаете незнакомые термины, сокращения или акронимы, сразу же останавливайтесь и ищите их определение где-нибудь в Wikipedia или в Google.

Работа с компилятором GCC

Если вы вдруг привыкли писать программы примерно по такому сценарию:

то возможно, вам необходимо разобраться что же действительно происходит в это время, и как это проделать вручную, без помощи IDE. Это поможет вам понять, зачем вообще нужна IDE и как вручную управлять компиляцией. IDE является очень тяжёлым, не всегда необходимым, и часто абсолютно ненужным в некоторой ситуации инструментом.

Итак. Начнём с простой программы, которую откомпилируем и попытаемся запустить. Я выбираю язык программирования С. Объяснять почему, пожалуй, не буду. Простейшую программу на языке C вы, наверное, уже видели:

int main()
{
  return 0;
}

Каждая программа, написанная на языке программирования С должна содержать функцию с именем main, возвращающую значение типа int. В случае успешного, безошибочного завершения работы программы, функция должна вернуть 0. В конце каждого файла последним символом должен быть символ перевода строки. Куда же записать этот код? Не спешите пожалуйста открывать IDE. Давайте для начала просто создадим текстовый файл, содержащий текст программы и более ничего. Как создать файл, содержащий данный текст? А легко, к тому же существует огромное количество способов. Вот наиболее простые для вас:

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

Но чтобы запустить программу, этого файла недостаточно. Нам необходимо получить исполняемый файл. Для того, чтобы из исходных кодов получить исполняемый файл, необходимо проделать некоторые действия. Мы воспользуемся компилятором GCC. В простейшем случае нужно ввести комманду

gcc
а в качестве аргументов передать ей имя файла, содержащего исходный код. Делается это примерно так:

$ gcc main.c

В результате в вашей директории должен появится файл с именем

a.out
, который является исполняемым и готовым к запуску. Для того, чтобы его запустить, необходимо ввести его имя, предварив его ./, чтобы указать, что мы запускаем файл из текущей директории а не из стандартных директорий, содержащих исполняемые файлы. Выглядит это так:

$ ./a.out

Если вы ничего не меняли в наших исходных кодах, то после запуска никакого вывода на экране вы не увидите. Давайте чуток поиграемся, чтобы лучше разобраться. Пускай наша программа будет спрашивать у игрока его имя и здороваться с ним, используя полученные сведения. Для этого чуток подкорректируем наш файл:

#include <stdio.h>

int main()
{
  char buffer[16];
  printf("Введите, пожалуйста, Ваше имя:\n");
  scanf("%s", buffer);
  printf("Здравствуйте, %s.\n", buffer);
  return 0;
}

Вот последовательность действий, которую необходимо выполнить, чтобы из исходных кодов на языке программирования С получить исполняемый файл:

Препроцессорная обработка нашего файла будет заключатся лишь в замене директивы

#include <stdio.h>
содержимым файла stdio.h. Вы можете убедится в этом, выполнив следующую команду:

$ gcc -E -o main.i main.c

По умолчанию GCC сразу выполняет все стадии, чтобы получить на выходе исполняемый файл. Аргумент

-E
сообщает ему, что необходимо остановится на стадии препроцессорной обработки и не начинать компиляцию. Аргумент
-o main.i
указывает имя выходного файла. Файлам, содержащим программный код на языке С, не подлежащий препроцессорной обработке, необходимо давать имена с суффиксом .i. Последние аргумент - имя файла, содержащего программный код на языке С, подлежащий процессорной обработке. В результате вы получите файл main.i, содержащий обработанный препроцессором исходный код. Советую вам ознакомится с его содержимым.

Теперь давайте скомпилируем получившийся файл и получим объектный файл. Для этого необходимо ввести:

$ gcc -c -o main.o main.i

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

$ gcc -o main main.o

В результате наших действий GCC вызывает линковщик, и тот связывает наш файл main.o со стандартной библиотекой С, содержащей необходимые нам функции printf, scanf. А также с некоторыми специальными объектными файлами, содержащими точку входа в программу с кодом, вызывающим функцию main и некоторые другие инструкции. В результате в папке с исходным кодом появился файл с именем main, который можно запустить:

$ ./main
Введите, пожалуйста, Ваше имя:
Кирилл
Здравствуйте, Кирилл.

Заметьте, что если ввести имя, превышающее по длине размер буффера, который мы указали в программе, произойдёт ошибка сегментации:

$ ./main
Введите, пожалуйста, Ваше имя:
Кирюшычичоночичек
Здравствуйте, Кирюшычичоночичек.
Segmentation fault (core dumped)

Поэтому всегда будьте осторожны при работе с буфферами. Даже если вы создаёте буффер очень большого размера, заведомо на порядок превышающий по размеру необходимый, взломщик обязательно пришлёт на ваш игровой сервер строку ещё более длинную, чтобы его уронить. А выход за пределы буффера в модуле ядра, например, может вызвать серьёзную ошибку, которая приведёт к прекращению работы операционной системы. В нашей ситуации необходимо указать функции scanf размер буффера. Прочитав пятнадцатый байт, функция прекратит считывание в буффер и таким образом не начнёт запись за его пределами:

$ cat > main.c
#include <stdio.h>

int main()
{
  char buffer[16];
  printf("Введите, пожалуйста, Ваше имя:\n");
  scanf("%16s", buffer);
  printf("Здравствуйте, %s.\n", buffer);
  return 0;
}
^Z
[1]+  Stopped                 cat - > main.c
$ gcc -o main main.c
$ ./main
Введите, пожалуйста, Ваше имя:
Кирюшычичоночичек
Здравствуйте, Кирюшычи.

Надеюсь, вы поняли, что написано сверху. Но я на всякий случай ещё раз объясню, что значат комманды. Сначала мы вызываем комманду

cat
без аргументов и перенаправляем её вывод в файл main.c с помощью оператора >.

Затем мы вводим текст программы, и не забываем символ конца файла ^Z.

Потом мы компилируем получившийся файл, который содержит исходный код в исполняемый файл:

gcc -o main main.c
.

Запускаем мы его с помощью комманды 

./main
.

Следующая часть будет про Make-файлы и SDL.

22 Декабря 2009 - 26 Декабря 2009

#C, #cat, #GCC, #gnu, #Linux, #nano, #sdl, #shell, #Unix, #x11

17 июля 2010 (Обновление: 19 июля 2010)