Skip to content

Home Программирование Многофайловые проекты
Многофайловые проекты

Современные программные проекты редко ограничиваются одним исходным файлом. Распределение исходного кода программы на несколько файлов имеет ряд существенных преимуществ перед однофайловыми проектами.

  • Использование нескольких исходных файлов накладывает на репозиторий (рабочий каталог проекта) определенную логическую структуру. Такой код легче читать и модернизировать.
  • В однофайловых проектах любая модернизация исходного кода влечет повторную компиляцию всего проекта. В многофайловых проектах, напротив, достаточно откомпилировать только измененный файл, чтобы обновить проект. Это экономит массу времени.
  • Многофайловые проекты позволяют реализовывать одну программу на разных языках программирования.
  • Многофайловые проекты позволяют применять к различным частям программы разные лицензионные соглашения.

Обычно процесс сборки многофайлового проекта осуществляется по следующему алгоритму:

 

1. Создаются и подготавливаются исходные файлы. Здесь есть одно важное : каждый файл должен быть целостным, т. е. не должен содержать незавершенных конструкций. Если в рамках проекта предполагается создание исполняемой программы, то в одном из исходных файлов должна присутствовать функция main ().

2. Создаются и подготавливаются заголовочные файлы. У заголовочных файлов особая роль: они устанавливают соглашения по использованию общих идентификаторов (имен) в различных частях программы. Если, например, функция func () реализована в файле а.с, а вызывается в файле Ь.с, то в оба файла требуется включить директивой #inciude заголовочный файл, содержащий объявление (прототип) нашей функции. Технически можно обойтись и без заголовочных файлов, но в этом случае функцию можно будет вызвать с произвольными аргументами, и компилятор, за отсутствием соглашений, не выведет ни одной ошибки. Подобный "слепой" подход потенциально опасен и в большинстве случаев свидетельствует о плохом стиле программирования.

3. Каждый исходный файл отдельно компилируется с опцией -с. В результате появляется набор объектных файлов.

4. Полученные объектные файлы соединяются компоновщиком в одну программу.

Если необходимо скомпоновать несколько объектных файлов (овл.о, OBJ2. о и т. д.), то применяют следующий простой шаблон:

$ gcc  -о OUTPOT_FILE ОВЛ.о 0BJ2.O   ....

Рассмотрим программу, которая принимает в качестве аргумента строку и переводит в ней все символы в верхний регистр, т. е. заменяет все строчные буквы на заглавные. Чтобы не усложнять пример, будем преобразовывать только латинские (англоязычные) символы.

Для начала создадим файл print_up.h, в котором будет находиться объявление (прототип) функции print_up{}. Эта функция переводит символы строки в верхний регистр и выводит полученный результат на экран.

Файл print_up.h

void print_up (const char * str) ;

Итак, объявление функции print_up() устанавливает соглашение, по которому любой исходный файл, включающий print_up.h директивой #include, обязан вызвать функцию print_up() с одним и только одним аргументом типа const char*. Теперь создадим файл print_up.c, в котором будет находиться тело функции print_up ().

Файл print.up с

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "print_up.h"
void print_up (const char * str) {
int i;
for (i =0; i < strlen (str); i++)
printf ("%c", toupper (str[i]));
printf ("\n"); }

Функция print_up () просматривает в цикле всю строку, посимвольно преобразовывая ее к верхнему регистру. Вывод также производится посимвольно. В заключение выводится символ новой строки. Функция toupper о, объявленная в файле ctype.h и являющаяся частью стандартной библиотеки языка С, возвращает переданный ей символ в верхнем регистре, если это возможно. Без дополнительных манипуляций эта функция не работает с русскими символами, но сейчас это не важно. Теперь создадим третий файл main.c, который будет содержать функцию main(), необходимую для компоновки и запуска программы.

Файл main.с

#include <string.h> 
#include <stdio.h>
# include "print_up.h"
int main (int argc, char ** argv) {
if (argc < 2) {
fprintf (stderr, "Wrong arguments\n"); return 1; }
print_up (argv[1]); return 0; }

Сначала вспомним, что аргументы командной строки передаются в программу через функцию main():

  • argc — целое число, содержащее количество аргументов командной строки;
  • argv — массив строк, в котором находятся аргументы.

Следует помнить, что в первый аргумент командной строки обычно заносится имя программы. Таким образом, argv[0] — это имя программы, argv[1] — первый переданный при запуске аргумент, argv[2] — второй аргумент и так далее до элемента argv[argc-i].

Вообще говоря, аргументы в программу можно передавать не только из командной оболочки. Поэтому понятие "аргументы командной строки" не всегда отражает действительное положение вещей. В связи с этим, чтобы избежать неоднозначности, будем в дальнейшем пользоваться более точной формулировкой "аргументы программы".

Теперь нужно собрать проект воедино. Сначала откомпилируем каждый файл с расширением . с:

$ gcc -с print_up.c 
$ gcc -с main.с

В результате компиляции должны появиться объектные файлы print_up.o и main.o, которые следует скомпоновать в один бинарный файл:

$ gcc -о printup print_up.o main.o

Если компилятор gcc вызывается с опцией -с, но без опции -о, то имя выходного файла получается заменой расширения .с на .о. Например, из файла foo.c получается файл foo.o.

Осталось только запустить и протестировать программу:

$ ./printup 
Wrong arguments
$ ./printup
Hello HELLO

Обратите внимание на то, что заголовочный файл print_up.h не компилируется. Заголовочные файлы вообще никогда отдельно не компилируются. Дело в том, что на стадии препроцессирования (условно первая стадия компиляции) все директивы #include заменяются на содержимое указанных в них файлов.

Комментарии (0)

RSS feed Comments

Написать комментарий

smaller | bigger

busy
 

Регистрация




Top