Как разбить программу на файлы c

Обновлено: 28.04.2024

Большая программа на C или C++ должна быть разделена на несколько файлов. Это делает каждый файл достаточно коротким для удобного редактирования, печати и т. д. Это также позволяет использовать часть кода, например. служебные функции, такие как обработчики связанных списков или код выделения массива, для совместного использования с другими программами.

Разделение по темам

Как правило, разделяйте свой код на естественные части программы. Каждый файл должен содержать группу функций, которые делают связанные вещи, например. манипулируют одной и той же структурой данных, связаны с одним и тем же классом или тесно связанной группой классов и т. д. Представьте себе другую программу, которая могла бы использовать некоторые из этих функций: включите в один файл все функции, которые потребуются этой другой программе. Или попробуйте написать описание того, что находится в каждом файле, на английском языке: хорошие разделы обычно можно описать лаконично.

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

Большая программа может быть разделена на несколько таких файлов, а также основной файл программы. Основной файл содержит функцию main, а также различные функции, которые не подходят больше нигде и/или кажутся специализированными для данной конкретной программы.

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

Компиляция

Чтобы скомпилировать многофайловую программу, сначала скомпилируйте каждый из неосновных файлов кода. Например, файл функций манипулирования массивами может быть скомпилирован следующим образом:

Флаг -c указывает компилятору создать выходной файл (который будет называться arrays.o), который можно слинковать с другими скомпилированными файлами. Это также предотвращает жалобы компилятора на отсутствие основной функции и на использование функций, которые не определены (т. е. потому что они определены в других файлах кода). Флаг -Wall включает все предупреждения компилятора: в целом хорошая идея.

Когда вы сделаете это для всего своего неосновного кода, у вас будет набор файлов с расширением .o, называемых "объектными файлами". Последним шагом в компиляции является компиляция основного файла с добавлением всех объектных файлов. Делается это следующим образом:

Флаг -o указывает компилятору создать выходной (исполняемый) файл с именем myprogram. Если вы опустите эту опцию, исполняемый файл будет называться a.out. Объектные файлы перечислены в командной строке после основного файла.

Связывая все эти программы вместе, компилятор C++ сопоставляет вызовы каждой функции с определениями этой функции. Это будет работать автоматически, даже если функция определена в другом файле кода. Компилятор будет жаловаться, если вы используете функцию, которая никогда не определена, например. если вы забыли включить один из ваших объектных файлов.

Прототипы функций

  • функция определена в одном файле, но вызывается в другом файле
  • две функции рекурсивно вызывают друг друга
  • вы хотите изменить порядок функций в файле, например. поставить основную функцию на первое место

Некоторые программисты обычно создают файл кода с прототипами для всех функций, определенных в этом файле, так что им не нужно беспокоиться о порядке, в котором они дают полный код функций.

Прототипы содержат только информацию о типе функции, но не ее фактический код. См. следующие примеры, в которых токен является определяемым пользователем классом.

Простой способ написать прототип — скопировать первую строку определения функции и добавить точку с запятой. Обратите внимание, что имена для входных параметров могут быть включены в прототип или опущены. Компилятор их не использует, но они могут быть полезны вам или другим людям, читающим ваш код.

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

Файлы заголовков

Объявления часто перемещаются в отдельные файлы, называемые файлами заголовков. Если фрагмент кода называется foo.C, соответствующий заголовочный файл традиционно называется foo.h. Вы можете дать ему другое имя, но делайте это только тогда, когда есть четкая причина для странного имени.Типичной причиной может быть то, что некоторая группа объявлений используется несколькими файлами кода, ни один из которых не может претендовать на первичное владение объявлениями.

Основная цель файлов заголовков — сделать определения и объявления доступными для функций более чем в одном файле. Например, если функциям из двух файлов требуется доступ к одной и той же глобальной константе (например, определению константы PI), эта переменная должна быть определена в заголовочном файле. И наоборот, если объявление предназначено для использования функциями только в одном файле кода, оставьте его в этом файле кода и не помещайте в файл заголовка.

Таким образом, ваш заголовочный файл может содержать такие элементы, как

определения глобальных переменных и глобальных констант

определения классов, структур, объединений, перечислимых типов

операторы typedef, используемые для создания имен типов

объявления функций, называемые "прототипами"

включить операторы для других файлов, включая файлы библиотек C/C++ (например, iostream.h, math.h)

Как правило, заголовочные файлы не должны содержать код функций. Исключения включают очень короткие функции, настолько короткие, что их можно включать в определение класса или объявлять как встроенные. Эти функции выполняют тривиальные действия, например увеличивают значение счетчика, возвращают большее из двух чисел или возвращают значение закрытого члена данных для класса.

Если функция делает что-то нетривиальное, она принадлежит файлу кода. Если функции в другом файле кода должны иметь возможность вызывать его, поместите прототип (объявление) функции, но не фактический код функции, в файл заголовка. (См. выше примеры прототипов.)

После создания заголовочного файла foo.h каждый файл кода, в котором используются определения из foo.h, должен содержать оператор включения. Это указывает компилятору читать код в foo.h при компиляции файла кода. Это как если бы материал из foo.h был вставлен в виде текста в ваш файл кода.

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

При использовании сложных наборов вложенных включаемых файлов программы могут в конечном итоге загрузить включаемый файл дважды. В таких случаях необходимо принимать особые меры предосторожности для предотвращения ошибок компилятора. Пока у вас не будет опыта программирования, лучше не загружать заголовочный файл более одного раза. Исключение: файлы системных заголовков, такие как iostream.h, обычно пишутся так, чтобы их можно было безопасно загружать несколько раз.

Если вы когда-нибудь хотели написать большую программу или программное обеспечение, самая распространенная ошибка новичка — сразу же приступить к работе и попытаться написать весь необходимый код в одной программе, а затем попытаться отладить или расширить ее позже.

Такой подход обречен на провал и обычно требует перезаписи с нуля.

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

Это не только упрощает нашу задачу, но и позволяет нам достичь абстракции от программиста высокого уровня, а также способствует повторному использованию кода.

Если вы проверите любой проект с открытым исходным кодом на GitHub, GitLab или любом другом подобном сайте, мы увидим, как большая программа «децентрализована» на множество подмодулей, где каждый отдельный модуль вносит свой вклад в определенный важная функция программы, а также различные члены сообщества открытого исходного кода объединяются для предоставления или поддержки таких файлов или репозиториев.

Теперь большой вопрос заключается в том, как «сломать» не теоретически, а ПРОГРАММНО.

Перейти к C/C++

Перейти к Python

Перейти к Java

В иллюстративных целях

Предположим, что у нас есть все основные вставки связанных списков внутри одной программы. Так как методов (функций) много, мы не можем загромождать программу, записывая все определения методов над обязательной основной функцией. Но даже если бы мы это сделали, может возникнуть проблема упорядочения методов, когда один метод должен быть перед другим и так далее.

Поэтому, чтобы решить эту проблему, мы можем объявить все прототипы в начале программы, за ними следует основной метод, а под ним мы можем определить их в любом конкретном порядке:

Полный связанный список.c

Компиляция кода. Мы можем скомпилировать приведенную выше программу следующим образом:

Основные проблемы в приведенном выше коде:
Мы уже можем видеть основные проблемы с программой, с кодом совсем непросто работать ни индивидуально, ни в группе.

  1. Необходимо просмотреть полный исходный файл, чтобы улучшить или улучшить некоторые функции.
  2. Невозможно повторно использовать программу в качестве основы для других проектов.
  3. Код очень загроможден и совсем не привлекателен, что затрудняет навигацию по коду.

В случае группового проекта или крупных программ описанный выше подход гарантированно увеличивает общие затраты, энергию и частоту неудач.

Правильный подход:

Эти строки обрабатываются препроцессором во время компиляции.

Мы можем вручную попытаться создать такую ​​библиотеку для наших собственных целей.

  1. Файлы «.h» содержат только объявления прототипов (такие как функции, структуры) и глобальные переменные.
  2. Файлы «.c/.cpp» содержат реальную реализацию (определения объявления в файлах заголовков)
  3. При компиляции всех исходных файлов убедитесь, что нет нескольких определений одних и тех же функций, переменных и т. д. для одного и того же проекта. (ОЧЕНЬ ВАЖНО)
  4. Используйте статические функции, чтобы ограничиться файлом, в котором они объявлены.
  5. Используйте ключевое слово extern для использования переменных, которые ссылаются на внешние файлы.
  6. При использовании C++ будьте осторожны с пространствами имен, всегда используйте namespace_name::function() во избежание конфликтов.

Разделение программы на более мелкие коды:
Глядя на приведенную выше программу, мы видим, как эту большую программу можно разделить на подходящие маленькие части, а затем с ними легко работать.

Вышеупомянутая программа имеет две основные функции:
1) Создание, вставка и сохранение данных в узлах.
2) Отображение узлов

Поэтому я могу разделить программу соответственно таким образом:
1) Основной файл -> Программа драйвера, Nice Wrap для модулей вставки и где мы используем дополнительные файлы.
2) Вставка -> The Настоящая реализация лежит здесь.

Принимая во внимание упомянутые важные моменты, программа делится на:

linkedlist.c -> Содержит программу-драйвер
insert.c -> Содержит код для вставки

linkedlist.h -> Содержит необходимые объявления узлов
insert.h -> Содержит необходимые объявления вставки узла

В каждом заголовочном файле мы начинаем с:

Для этого примера программы:

insert.h -> Содержит объявление вставки узла, а также объявление самого узла.

Очень важно помнить, что компилятор может видеть объявления в заголовочном файле, но если вы попытаетесь написать код, ВКЛЮЧАЮЩИЙ определение объявления, объявленного в другом месте, это приведет к ошибке, поскольку компилятор компилирует каждый файл .c по отдельности, прежде чем перейти к этап связывания.

linkedlist.h -> Вспомогательный файл, содержащий объявления Node и его отображения, которые должны быть включены для файлов, которые их используют.

linkedlist.c -> Простая оболочка, содержащая бесконечный цикл, предлагающий пользователю вставить целочисленные данные в требуемые позиции, а также метод, отображающий список.

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

Помня вышеизложенное, вы должны тщательно разделить на подходящие подпрограммы.

связанный список.h

insert.h

insert.c

связанный список.c

Наконец, мы сохраняем их все и компилируем следующим образом.

Вуаля, он успешно скомпилирован, давайте просто проверим работоспособность, на всякий случай:

Вывод:

В основном это остается неизменным для C++, за исключением обычных изменений языковых функций/реализации.

Питон

Тут не так уж и сложно. Обычно первое, что нужно сделать, это создать виртуальную среду. Это необходимо для предотвращения поломки множества скриптов из-за различных зависимостей версий и тому подобного. Например, вы можете использовать версию 1.0 какого-либо модуля для одного проекта, но в этой последней версии устарела функция, доступная в 0.9, и вы предпочитаете использовать старую версию для этого нового проекта или просто хотите обновить библиотеки, не нарушая их. старые и существующие проекты. Решение представляет собой изолированную среду для каждого отдельного проекта/сценария.

Как установить Virtual Env:
Используйте pip или pip3 для установки virtualenv, если он еще не установлен:

Настройка изолированной среды для каждого проекта/скрипта:
Далее перейдите в какой-либо каталог для хранения ваших проектов, а затем:

Теперь вы можете использовать pip для установки всех нужных модулей, и они действуют как автономные для этого изолированного проекта, и вам не нужно беспокоиться о нарушении общесистемного сценария. например: с активированным виртуальным окружением и источником,

Важно создать явный пустой файл с именем:

Это делается для того, чтобы рассматривать каталог как содержащий пакеты и получать доступ к подмодулям внутри каталога. Если вы не создадите такой файл, Python не будет явно искать подмодули внутри каталога проекта, и любая попытка доступа к ним приведет к ошибке.

Импорт ранее сохраненных модулей в новые файлы:
Теперь вы можете начать импорт ранее сохраненных модулей в новые файлы любым из способов:

Новый год (кстати, удачного нового года) новый учебник по языку C. Мы начинаем год с учебника по языку C, где мы рассмотрим, как разделить текстовый файл на несколько файлов. В предыдущих руководствах мы уже рассмотрели чтение и запись текстовых файлов на примерах C и поиск в текстовых файлах, поэтому в этом руководстве мы будем опираться на эти руководства.

Разделение текстовых файлов

Давайте сначала посмотрим на исходный код нашей программы разделения текста, прежде чем мы посмотрим, что мы делаем в примере:

Пример начинается с включения заголовочного файла stdio.h. В функции main() мы объявляем два указателя на файл, один для чтения и один для записи. Затем создается буфер с максимальным размером строки 128 символов (или измените его на любой другой подходящий максимальный размер строки). Создается массив символов для хранения выходного имени файлов, которые разделены, и создаются некоторые счетчики (счетчик файлов и счетчик строк).

После объявления какой-либо переменной, которая нам нужна, мы начинаем с открытия файла для чтения. В операторе if мы проверяем, правильно ли прошло открытие.

В следующих операторах мы создаем имя выходного файла, добавляя значение счетчика файлов к строке file_part, чтобы получить file_part1, file_part2 и т. д. Затем мы открываем первый выходной файл для записи в следующем операторе.

Теперь, когда мы выполнили все инициализации, мы можем взглянуть на трудную часть программы — цикл while. Цикл while будет выполняться до тех пор, пока в исходном файле не останется строк. Функция fgets получает эти строки (одну за другой) из исходного файла. В операторе if мы проверяем количество обработанных строк, если оно равно 5, то обрабатывается блок кода, в противном случае строки записываются в выходной файл. Если оператор if верен, мы хотим переключиться на новый выходной файл.

В операторе if мы сначала закрываем выходной файл и сбрасываем счетчик строк. Затем мы делаем плюс один из счетчика файлов. Затем в операторе sprintf создается новое имя выходного файла. Мы открываем новый файл для записи и выполняем простую проверку ошибок в операторе if. Как указывалось ранее, файлы будут создаваться до тех пор, пока во входном файле не останется строк.

ПРИМЕЧАНИЕ: не добавляйте слишком много строк во входной файл test.txt, иначе количество разделенных файлов может стать очень большим! Вы предупреждены ;-).

Очистка исходного кода
Последнее, что нам нужно сделать, это очистить наш пример c с разделением файлов, удалив двойные части программы и переместив их в функцию, которую мы можем вызвать. Сначала взгляните на результат этого действия по очистке:

Если вы сравните первый пример исходного кода со вторым, вы увидите, что единственное, что мы сделали, — это перенесли создание и открытие выходного файла в функцию. Давайте посмотрим на функцию openforwrite().

Функция openforwrite() принимает целое число (счетчик файлов) и возвращает указатель на открытый файл с именем, созданным в операторе sprintf.

Остальная часть программы почти такая же, как в предыдущем примере. Добавлены только два вызова openforwrite() для замены управляющих операторов sprintf, open file и if.

Это все для этого руководства по C, до следующего раза!

Эта запись была опубликована в Учебники по C. Вы можете следить за любыми ответами на эту запись через ленту RSS 2.0. И комментарии и запросы в настоящий момент закрыты. Твитнуть это! или используйте, чтобы поделиться этим сообщением с другими.

В настоящее время есть 4 ответа на «Учебник по C — Разделение текстового файла на несколько файлов»

Почему бы не сообщить нам, что вы думаете, добавив свой комментарий!

    Паплаукиас, 7 января 2013 г.:

Привет,
отличные уроки. работал над ними. В этом руководстве я обнаружил небольшую ошибку.

в части 'sprintf(fileoutputname, «file_part%d», filecounter)' было бы удобнее написать «file_part%d.txt», тогда после запуска программы были бы файлы .txt.

но в остальном отличный туториал. продолжай в том же духе

@paplaukias: да, можно написать «file_part%d.txt» вместо «file_part%d» (особенно верно для систем Windows). Но в системах Unix или Linux мы обычно не используем расширения (в данном случае я создал исходный код в системе Red Hat). Оба хороши, поэтому используйте тот, который вам более понятен. Удачи!.

Большое спасибо за ваши уроки.
Я работал с C, когда учился в университете. Прошло несколько лет, и мне нужно снова сделать программу (найти строку в строке, удалить ненужные слова и записать ее в файл). Я провожу эти выходные, пытаясь вспомнить кое-что из C. Учебники здесь действительно хорошо объяснены и помогли мне улучшить мой первоначальный код.

Привет, я новичок в языке c, мне нужен код для одной программы… пожалуйста, помогите
WAP прочитать строку из текстового файла, который содержит:-
place=usa
place=japan < br />place=canada
bird=parrot
bird=peacock
————————————————
На выходе должно получиться так
place=США,Япония,Канада
bird=попугай,павлин

Отдельное руководство по компиляции

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

До сих пор всякий раз, когда вы хотели скомпилировать программу, вы компилировали файл, отображаемый в буфере Emacs. Скомпилировать этот файл. Опция меню компиляции Emacs (или, что то же самое, M-x compile ) является удобной функцией, когда вся ваша программа содержится в одном файле. Конечно, можно скомпилировать файл полностью независимо от Emacs.

Загрузить hello.c . Это простая программа, которая выводит ``Hello world'', и вы можете скомпилировать и запустить ее, даже не отображая ее в Emacs. Чтобы скомпилировать его, перейдите в окно Unix Shell, подключитесь к каталогу, в который вы загрузили программу, и введите следующую команду:

Эта команда приведет к созданию нового файла. Используйте команду ls, чтобы отобразить ваш каталог. Какого файла не было раньше?

На данный момент у вас еще нет исполняемой программы, которую вы можете запустить. Следующим шагом является связывание объектного файла для создания такого исполняемого файла. Для этого введите в окно UNIX Shell следующее:

Связывание связывает ваш скомпилированный код с некоторым дополнительным кодом, специфичным для C. Еще раз, новый файл был создан в вашем каталоге. Перечислите каталог. Какой новый файл вы нашли?

Теперь, когда у вас есть исполняемый файл, вы можете запустить его обычным способом. Просто введите «hello» в окно UNIX Shell.

<ПР> Скомпилируйте файл ``.c'', содержащий исходный код, с помощью такой команды, как

Если вся ваша программа содержится в одном файле, вы можете выполнить компиляцию и компоновку за один шаг, введя команду

Это именно то, что делает Emacs, когда компилирует для вас программу. До сих пор мы старались выполнять компиляцию в два отдельных этапа, чтобы вы могли работать с программами, состоящими из более чем одного файла. Мы подробнее рассмотрим эту тему в следующем разделе.

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

Попробуйте придумать несколько причин, почему это может быть хорошей идеей.

Теперь давайте посмотрим на программу, разделенную на несколько файлов. Загрузите main.c и help.c и просмотрите их в Emacs. Обратите внимание, что main.c использует две функции, определенные в ``help.c''.

Есть ли способ скомпилировать эту программу из Emacs? Попробуйте скомпилировать ``main.c'' таким образом. Что происходит?

Теперь попробуйте скомпилировать буфер, содержащий ``help.c''. Что происходит?

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

Теперь сделайте это сами. Скомпилируйте, свяжите и запустите программу.

Когда вам это удастся, проведите следующий эксперимент. Слегка измените основную программу, чтобы вызывать две функции в противоположном порядке. Как вы думаете, что будет задействовано в перекомпиляции, компоновке и запуске новой версии программы?

Скомпилируйте, свяжите и запустите измененную программу. (Не забудьте перед этим сохранить ``main.c''!)

Проблема остается. Всякий раз, когда вы компилируете ``main.c'', компилятор жалуется, что ``sum'' и ``product'' являются необъявленными функциями. Даже в этом случае компоновщику удается собрать исполняемый файл. Зачем нам беспокоиться о предупреждении компилятора, если мы все равно можем получить исполняемую программу?

На самом деле решить эту проблему не так уж и сложно. Просто добавьте следующие две строки в ``main.c'' над основной процедурой.

Где вы видели эти строки раньше?

Сохраните ``main.c'' и перестройте исполняемый файл. Если вы все сделали правильно, ошибки должны исчезнуть.

Это неуклюжий способ объявления функций, реализованных в отдельном файле. Можете ли вы придумать какие-нибудь проблемы?

Вот как мы можем решить обе эти проблемы. Создайте новый файл, который не содержит ничего, кроме объявлений функций в ``help.c''. Собственно, такой файл мы уже создали: скачайте help.h и посмотрите на него. Что там помимо деклараций?

Теперь измените ``main.c'', удалив объявления, которые вы добавили, и заменив их строкой

Как вы, вероятно, помните, это указание компилятору вести себя так, как если бы содержимое файла ``help.h'' действительно было введено здесь. Обратите внимание, что мы заключили имя файла в кавычки, а не в угловые скобки, как, например, в случае с stdio.h. Почему?

Сохраните ``main.c'', скомпилируйте, свяжите и запустите его. Как это решает проблему (1), указанную выше?

Однако мы до сих пор не решили проблему (2). Ничто не мешает нам изменить объявления в ``help.h'', сделав его несовместимым с ``help.c''.

К счастью, есть способ решить и эту проблему. Поместите строку

в верхней части ``help.c''. Теперь сохраните ``help.c'' и пересоберите исполняемый файл. Вы не должны столкнуться с ошибками.

Теперь снова отредактируйте ``help.c'', изменив одно из объявлений. Например, измените ``сумму'' так, чтобы она принимала в качестве аргумента число с плавающей запятой, а не целое число. Сохраните и перекомпилируйте ``help.c''. Что происходит?

Подводя итог, вот рекомендуемая процедура работы с многофайловыми программами на C.

<ПР>

  • Для каждого файла ``file.c'', содержащего функции, которые будут использоваться в другом месте, создайте файл ``file.h'', содержащий информацию заголовка и комментарии для этих функций.
  • Поставить линию

    в начале всех файлов, использующих функции из ``file.c''. Таким образом, компилятор предупредит вас, если вы неправильно используете функции.

    Затем вы можете отдельно скомпилировать файлы ``.c'', а затем скомпоновать их вместе для создания исполняемого файла. В следующем уроке мы рассмотрим файлы Makefile, которые обеспечивают более удобный способ компиляции и компоновки программ с несколькими файлами.

    Читайте также: