Как структурировать функции C для создания переиспользуемой библиотеки утилит

6

Вы написали код. Он работает. Но теперь вы хотите использовать генератор rand() или процедуру bubble_sort в совершенно другом проекте. Копирование и вставка — это лень. Это также хрупкий подход. Когда вы обновляете одну версию, вам приходится искать каждую копию кода.

Профессиональное решение? Создайте библиотеку.

В экосистеме C библиотека — это не магический черный ящик. Это всего лишь два файла, выполняющие разные роли. Вам нужен заголовочный файл (.h ) и исходный файл (.c ). Заголовочный файл сообщает остальному миру, какие функции существуют. Исходный файл выполняет реальную работу.

Создание интерфейса: util.h

Заголовочный файл действует как контракт. Он не содержит логики. Он содержит обещания. Константы, типы и прототипы функций.

Создайте файл с именем util.h.

Ключевые слова extern выполняют тяжелую работу. Они говорят компилятору: «Не ищи реализацию здесь. Я найду её позже во время линковки». Если вы застряли на устаревшем компиляторе, предшествующем современным стандартам C, удалите параметры из bubble_sort. Старые компиляторы такие придирчивые.

Сокрытие механизмов: util.c

Теперь сам код. Сохраните это как util.c.

Обратите внимание на директиву include. Мы используем кавычки ("util.h" ), а не угловые скобки ( ). Разница имеет значение. Кавычки ищут в текущей директории. Угловые скобки ищут в системных директориях. Этот файл локальный. Он принадлежит здесь.

Вот тонкий момент: rand_seed. Он находится в util.c. Его нет в util.h. Это сокрытие информации в действии. Код, использующий эту библиотеку, может вызывать rand(), но не может напрямую обращаться к seed (зерну). Состояние инкапсулировано. Для строгого контроля вы можете добавить ключевое слово static перед определением переменной. Это надежно блокирует доступ. Никакого внешнего доступа. Точка.

Использование библиотеки: main.c

Теперь напишите программу, которая её использует. Сохраните это как main.c.

Короче? Да. Чище? Безусловно. Основная программа не заботится о том, как rand() генерирует числа. Она просто требует число. Это разделение и есть вся суть.

Компиляция и линковка

Как превратить эти отдельные файлы в работающий исполняемый файл? Вы следуете трехшаговому танцу.

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

Флаг -c здесь обязателен. Он говорит компилятору остановиться после создания объектного файла. Он создает util.o. Этот файл содержит машинный код, но он неполный. В нем нет функции main. Он не может работать самостоятельно. Это компонент, а не продукт.

Далее скомпилируйте вашу основную программу:

Это генерирует main.o. Опять же, просто машинный код. Все еще без логики библиотеки.

Наконец, свяжите их вместе.

Эта команда берет оба объектных файла и сшивает их в один исполняемый файл с именем main. Теперь у вас есть полная программа. Запустите её с помощью ./main (или просто main, если вы используете Windows или она добавлена в ваш PATH).

Это много шагов для двух функций. Но представьте, если бы вы делали это для проекта с пятьюдесятью файлами. Вы компилируете каждый файл .c один раз. Вы связываете их вместе. Изменили функцию в библиотеке? Перекомпилируйте только этот файл. Пересвяжите. Остальное остается нетронутым.

Makefiles автоматизируют этот