<< Назад Вернуться к списку Дальше >>

4. Создание собственных библиотек функций. Линковка библиотек. Ведение больших проектов. План. 1. Создание своей библиотеки функций. 2. Использование своей библиотеки. 3. Механизм связывания файлов. 4. Линковка библиотек. 5. Ведение проекта с большим количеством файлов. 6. Написание консолькой игры про три кучи с созданием библиотеки для игры. Вспомним использование стандартных библиотек в своей программе. Например, функции ввода/вывода такие как printf и scanf, хранятся в библиотеке stdio.h. Т.е. перед их использованием надо написать в файле следующую строчку: #include <stdio.h> Препроцессор заменит ее на файл stdio.h, находящийся в папке со стандартными библиотеками. Но часто по самым разным причинам оказывается необходимым создание собственных библиотек. Как правило, библиотеку можно рассматривать как набор функций. Для начала мы и займемся созданием некоторого набора функций. Что для этого нужно? Первым делом надо создать файл, определяющий библиотеку. Это будет *.h файл. В нем будут содержаться прототипы функций библиотеки. Поясню, что такое прототип функции. Если наша функция записывается так: int func(int a, int b) { return a + b; } то ее прототипом будет: int func(int a, int b); Т.е. вместо тела функции в прототипе ставится ;. Зачем они нужны? Для ответа на этот вопрос надо подробнее рассмотреть процесс компиляции файла. Компиляция проекта состоит их двух частей: компиляция каждого отдельного модуля (файл .cpp) и линковка (соединение) скомпилированных модулей и внешних библиотек в один .exe файл. На этапе компилирования отдельного модуля создается файл .obj содержащий двоичный код функций, описанных в данном модуле и неинициализированными ссылками на другие функции, содержащиеся в других модулях. На этом этапе комписятор не интересует, существует ли в действительности эти функции (да это он и не может определить, он же не знает ничего о других модулях), важно только, чтобы было дано определение этих функций, т.е. их прототип. По этому прототипу компилятор определяет кол-во и типы параметров, передываемых функции, и тип возвращаемого ею значения. После компиляции всех модулей запускается линковщик, который инициализирует непроинициализированные ссылки на функции. Т.е. из всех файлов .obj составляет файл .exe, записывая в него список всех используемых во всех модулях функций и куски двоичного кода этих функций. Рассмотрим пример. Пусть нам надо создать свою библиотеку, содержащую три функции: вывод строки на экран, ввод с клавиатуры целого числа и сравнения двух целых чисел. Тогда код этой библиотеки будет выглядеть так: // файл lib.h void printStr(char *str); int getInt(); bool sravIntAndInt(int i1, int i2); // файл lib.cpp #include <stdio.h> #include "lib.h" void printStr(char *str) { printf("%s", str); } int getInt() { int i; scanf("%d", &i); return i; } bool sravIntAndInt(int i1, int i2) { if (i1 == i2) return true; else return false; } При компиляции этого модуля будет создан файл lib.obj, в котором будет список реализованный в нем фунций, состоящий из printStr, getInt и sravIntAndInt. В коде этих функций будут ссылки на функции printf и scanf, пока непроинициализированные. Чтобы этот модуль скомпилировался понадобилось включить в него их определения. Это сделано с помощью инструкции #include <stdio.h>, т.к. файл stdio.h содержит прототипы необходимых функций. Далее рассмотрим пример использования созданной библиотеки. // файл main.cpp #include "lib.h" void main() { int i1, i2; i1 = getInt(); i2 = getInt(); if (sravIntAndInt(i1, i2)) printStr("i1 = i2\n"); else printStr("i1 != i2\n"); } При компиляции этого модуля создается файл main.obj, в котором список реализованных функций будет ограничен функцией main. Также будут непроинициализированные ссылки на функции getInt, sravIntAndInt и printStr, определение которых содержится в файле lib.h, который включается с помощью инструкции #include "lib.h". Других функций в этом модуле не используется, поэтому никаких других библиотек присоединять не нужно. Далее для создания main.exe запускается линковщик, который соединяет файлы lib.obj, main.obj и библиотеки со стандартными функциями. Для этого линковщик соединяет списки функций из разных модулей в один и проверяет непроинициализированные ссылки на функции. Т.е. в начало файла main.exe после служебной информации он записывает функции из списков функций всех модулей. После списка он записывает двоичный код самих функций. Таким образом код программы в принципе готов. Но еще неизвестно все ли функции, вызываемые в программе, находятся в этом списке. Поэтому он проверяет все ссылки на функции в коде и, если какой-то из этих функций в списке нет, выдает ошибку. Приведу пример: // файл lib.h void printStr(char *str); // вывод строки на экран void ptintStrLn(char *str); // вывод строки с переводом строки на экран // файл lib.cpp #include <stdio.h> void printStr(char *str) { printf("%s", str); } // файл main.cpp #include "lib.h" void main() { printStr("str1"); printStrLn("str2"); } Данный пример, состоящий из двух модулей, после компиляции будет содержать два .obj файла: lib.obj и main.obj. При линковке он объединит их списки функций в один: void printStr(char *str); // из файла lib.obj, других функций в этом файле нет, т.к. файл lib.cpp не содержит определений других функций. void main(); // из файла main.obj ... здесь написан код функции printStr ... здесь написан код функции main ... здесь написан код функций из стандартных библиотек При проверке ссылок линковщик обнаружит, что функция main ссылается на функцию printStrLn, которой нет в списке с выдаст сообщение об ошибке. Разбиение на отдельные библиотеки просто необходимо при написании больших программ. Это происходит по очень многим причинам. И самая главная из них - это понятность кода. Если программа разбита на логичные куски, которые находятся в разных файлах и занимаются выполнением специфичной работой, то человеку, даже незнакомому с ней будет гораздо легче разобраться, чем в программе, написанной в одном файле длиной в 10000 строк.

<< Назад Вернуться к списку Дальше >>

Хостинг от uCoz