/* Открыть файл не удалось. Освобождаем буфер
перед выходом. */
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
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.а.