/* Открыть файл не удалось. Освобождаем буфер
перед выходом. */
free(buffer);
return NULL;
}
/* Чтение данных. */
bytes_read = read(fd, buffer, length);
if (bytes_read != length) {
/* Чтение не удалось. Освобождаем буфер и закрываем файл
перед выходом. */
free(buffer);
close(fd);
return NULL;
}
/* Все прошло успешно. Закрываем файл и возвращаем буфер
в программу. */
close(fd);
return buffer;
}
При завершении программы операционная система Linux освобождает выделенную память, ссылки на открытые файлы и большинство других ресурсов, поэтому перед вызовом функции exit()
нет необходимости удалять буферы и закрывать файлы. Но некоторые другие совместно используемые ресурсы приходится все же освобождать вручную. В частности, это относится к временным файлам и совместным буферам памяти: они способны "пережить" программу.
2.3. Создание и использование библиотек
Практически со всеми программами компонуется одна или несколько библиотек. К любой программе, использующей функции языка С (например, printf()
или malloc()
), подключается библиотека времени выполнения. Если у программы есть графический интерфейс, вместе с ней компонуются библиотеки функций работы с окнами. Когда программа обращается к СУБД, она делает это посредством функции библиотеки, предоставленной разработчиком данной СУБД.
В каждом из перечисленных случаев необходимо решить, как компоновать библиотеку:
2.3.1. Архивы
Архив создается посредством команды ar
. Архивные файлы традиционно имеют расширение .a
, а не .o
, которое закреплено за отдельными объектными файлами. Вот как объединить файлы test1.o
и test2.o
в единый архив libtest.a
:
% ar cr libtest.a test1.o test2.o
Флаги cr
сообщают команде ar
о необходимости создать архив.[8] Теперь можно подключать этот архив к программам с помощью флага -ltest
компилятора gcc
или g++
, как описывалось в разделе 1.2.2, "Компоновка объектных файлов".
Обнаруживая в командной строке архив, компоновщик ищет в нем определения всех символических констант (функций или переменных), на которые дается ссылка в уже обработанных объектных файлах. Объектные файлы, содержащие определения этих констант, извлекаются из архива и включаются в исполняемый файл. В связи с тем что компоновщик просматривает архив один раз, архивные файлы нужно указывать в конце командной строки. Предположим, например, что имеются два файла: test.c
(листинг 2.7) и app.c
(листинг 2.8).
int f() {
return 3;
}
int main() {
return f();
}
Теперь допустим, что файл test.o
включен вместе с другими объектными файлами в архив libtest.a
. Тогда следующая команда не будет работать:
% gcc -о app -L. -ltest app.о
app.о: In function 'main':
app.о(.text+0x4): undefined reference to 'f'
collect2: ld returned 1 exit status
Как следует из сообщения об ошибке, несмотря на то что файл libtest.а
содержит определение функции f()
, компоновщик не нашел ее. Это объясняется тем. что компоновщик анализирует свои аргументы последовательно, слева направо, просматривая архив сразу же, как только он встречается в командной строке. На тот момент компоновщик еще не знал, что в дальнейшем ему встретится ссылка на функцию f()
. Если сделать небольшую перестановку, все заработает:
% gcc -о app арр.о -L. -ltest
Теперь наличие в файле app.о
ссылки на функцию f()
заставляет компоновщик включить в программу объектный файл test.o
из архива libtest.а
.