Как поместить функции в отдельный файл c
Обновлено: 21.11.2024
В последней главе объяснялись стандартные устройства ввода и вывода, поддерживаемые языком программирования C. В этой главе рассказывается, как программисты C могут создавать, открывать, закрывать текстовые или двоичные файлы для хранения своих данных.
Файл представляет собой последовательность байтов, независимо от того, является ли он текстовым или двоичным файлом. Язык программирования C обеспечивает доступ к функциям высокого уровня, а также к низкоуровневым (на уровне ОС) вызовам для обработки файлов на ваших устройствах хранения. В этой главе вы познакомитесь с важными функциями управления файлами.
Открытие файлов
Вы можете использовать функцию fopen() для создания нового файла или для открытия существующего файла. Этот вызов инициализирует объект типа FILE, который содержит всю информацию, необходимую для управления потоком. Прототип вызова этой функции выглядит следующим образом:
Здесь имя файла — это строковый литерал, который вы будете использовать для именования файла, а режим доступа может иметь одно из следующих значений —
Открывает существующий текстовый файл для чтения.
Открывает текстовый файл для записи. Если его нет, то создается новый файл. Здесь ваша программа начнет записывать содержимое с начала файла.
Открывает текстовый файл для записи в режиме добавления. Если его нет, то создается новый файл. Здесь ваша программа начнет добавлять содержимое в существующее содержимое файла.
Открывает текстовый файл как для чтения, так и для записи.
Открывает текстовый файл как для чтения, так и для записи. Сначала он усекает файл до нулевой длины, если он существует, иначе создает файл, если он не существует.
Открывает текстовый файл как для чтения, так и для записи. Он создает файл, если он не существует. Чтение начнется с начала, но запись может быть только добавлена.
Если вы собираетесь работать с бинарными файлами, вы будете использовать следующие режимы доступа вместо упомянутых выше —
Закрытие файла
Чтобы закрыть файл, используйте функцию fclose(). Прототип этой функции —
Функция fclose(-) возвращает ноль в случае успеха или EOF, если при закрытии файла возникла ошибка. Эта функция фактически сбрасывает все данные, все еще ожидающие в буфере, в файл, закрывает файл и освобождает всю память, используемую для файла. EOF — это константа, определенная в заголовочном файле stdio.h.
Стандартная библиотека C предоставляет различные функции для чтения и записи файла, посимвольно или в виде строки фиксированной длины.
Запись файла
Ниже приведена простейшая функция для записи отдельных символов в поток —
Функция fputc() записывает символьное значение аргумента c в выходной поток, на который ссылается fp. Он возвращает письменный символ, записанный в случае успеха, иначе EOF, если есть ошибка. Вы можете использовать следующие функции для записи строки с завершающим нулем в поток —
Функция fputs() записывает строку s в выходной поток, на который ссылается fp. В случае успеха он возвращает неотрицательное значение, в противном случае в случае какой-либо ошибки возвращается EOF. Вы также можете использовать функцию int fprintf(FILE *fp,const char *format, . ) для записи строки в файл. Попробуйте следующий пример.
Убедитесь, что у вас есть каталог /tmp. Если это не так, то прежде чем продолжить, вы должны создать этот каталог на своем компьютере.
Когда приведенный выше код скомпилирован и выполнен, он создает новый файл test.txt в каталоге /tmp и записывает две строки, используя две разные функции. Давайте прочитаем этот файл в следующем разделе.
Чтение файла
Ниже приведена простейшая функция для чтения одного символа из файла —
Функция fgetc() считывает символ из входного файла, на который ссылается fp. Возвращаемое значение — это прочитанный символ, или в случае какой-либо ошибки он возвращает EOF. Следующая функция позволяет прочитать строку из потока —
Функция fgets() считывает до n-1 символов из входного потока, на который ссылается fp. Он копирует прочитанную строку в буфер buf, добавляя нулевой символ для завершения строки.
Если эта функция встречает символ новой строки '\n' или конец файла EOF до того, как будет прочитано максимальное количество символов, она возвращает только символы, прочитанные до этой точки, включая символ новой строки. Вы также можете использовать функцию int fscanf(FILE *fp, const char *format, . ) для чтения строк из файла, но она останавливает чтение после обнаружения первого символа пробела.
Когда приведенный выше код компилируется и выполняется, он считывает файл, созданный в предыдущем разделе, и выдает следующий результат —
Давайте немного подробнее рассмотрим, что здесь произошло. Во-первых, fscanf() читала только это, потому что после этого она столкнулась с пробелом, второй вызов для fgets(), который читает оставшуюся строку, пока не встретит конец строки. Наконец, последний вызов fgets() полностью считывает вторую строку.
Двоичные функции ввода/вывода
Есть две функции, которые можно использовать для двоичного ввода и вывода —
Обе эти функции следует использовать для чтения или записи блоков памяти — обычно это массивы или структуры.
Отдельное руководство по компиляции
Все программы, которые мы рассматривали до этого момента, содержались в одном файле. Это скорее исключение, чем правило. По мере того, как программы становятся больше, важно распределять их по файлам разумного размера. В этом уроке мы рассмотрим, почему это важно и как это можно сделать.
До сих пор всякий раз, когда вы хотели скомпилировать программу, вы компилировали файл, отображаемый в буфере 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''. Таким образом, компилятор предупредит вас, если вы неправильно используете функции.
Затем вы можете отдельно скомпилировать файлы ``.c'', а затем скомпоновать их вместе для создания исполняемого файла. В следующем уроке мы рассмотрим файлы Makefile, которые обеспечивают более удобный способ компиляции и компоновки программ с несколькими файлами.
И скрипты, и функции позволяют повторно использовать последовательности команд, сохраняя их в программных файлах. Скрипты — самый простой тип программ, поскольку они хранят команды точно так же, как вы вводите их в командной строке. Функции обеспечивают большую гибкость, прежде всего потому, что вы можете передавать входные значения и возвращать выходные значения. Например, эта функция с именем fact вычисляет факториал числа ( n ) и возвращает результат ( f ).
Этот тип функции должен быть определен в файле, а не в командной строке. Часто вы сохраняете функцию в отдельном файле. В этом случае лучше всего использовать одно и то же имя для функции и файла (в этом примере, fact.m ), поскольку MATLAB ® связывает программу с именем файла. Сохраните файл либо в текущей папке, либо в папке на пути поиска MATLAB.
Вы можете вызвать функцию из командной строки, используя те же правила синтаксиса, которые применяются к функциям, установленным вместе с MATLAB. Например, вычислите факториал 5.
Начиная с версии R2016b, еще одним вариантом хранения функций является их включение в конец файла сценария. Например, создайте файл с именем mystats.m с несколькими командами и двумя функциями: fact и perm. Скрипт вычисляет перестановку (3,2).
Вызов сценария из командной строки.
Синтаксис определения функции
Первая строка каждой функции — это оператор определения, который включает следующие элементы.
ключевое слово функции (обязательно)
Используйте символы нижнего регистра для ключевого слова.
Выходные аргументы (необязательно)
Если ваша функция возвращает один вывод, вы можете указать имя вывода после ключевого слова функции.
Если ваша функция возвращает более одного вывода, заключите имена вывода в квадратные скобки.
Если вывода нет, его можно не указывать.
Или вы можете использовать пустые квадратные скобки.
Название функции (обязательно)
Действительные имена функций подчиняются тем же правилам, что и имена переменных. Они должны начинаться с буквы и могут содержать буквы, цифры или символы подчеркивания.
Примечание
Чтобы избежать путаницы, используйте одно и то же имя как для файла функции, так и для первой функции в файле. MATLAB связывает вашу программу с именем файла, а не с именем функции. Файлы сценариев не могут иметь то же имя, что и функция в файле.
Входные аргументы (необязательно)
Если ваша функция принимает какие-либо входные данные, заключите их имена в круглые скобки после имени функции. Разделяйте входные данные запятыми.
Если входные данные отсутствуют, скобки можно опустить.
Совет
Когда вы определяете функцию с несколькими входными или выходными аргументами, сначала перечислите все необходимые аргументы. Такой порядок позволяет вызывать функцию без указания необязательных аргументов.
Содержимое функций и файлов
Тело функции может включать допустимые выражения MATLAB, операторы потока управления, комментарии, пустые строки и вложенные функции. Любые переменные, которые вы создаете в функции, хранятся в рабочей области, специфичной для этой функции, которая отделена от базовой рабочей области.
Файлы программы могут содержать несколько функций. Если файл содержит только определения функций, первая функция является основной функцией, и это функция, которую MATLAB связывает с именем файла. Функции, которые следуют за основной функцией или кодом скрипта, называются локальными функциями. Локальные функции доступны только внутри файла.
Конец операторов
Функции заканчиваются оператором end, концом файла или строкой определения локальной функции, в зависимости от того, что наступит раньше. Оператор end требуется, если:
Любая функция в файле содержит вложенную функцию (функция, полностью содержащаяся внутри своего родителя).
Эта функция является локальной функцией в файле функций, и любая локальная функция в файле использует ключевое слово end.
Функция — это группа операторов, которые вместе выполняют задачу. Каждая программа на C имеет по крайней мере одну функцию main(), и все самые простые программы могут определять дополнительные функции.
Вы можете разделить свой код на отдельные функции. Как вы разделяете свой код между различными функциями, зависит от вас, но логически разделение таково, что каждая функция выполняет определенную задачу.
Объявление функции сообщает компилятору об имени функции, типе возвращаемого значения и параметрах. Определение функции содержит фактическое тело функции.
Стандартная библиотека C предоставляет множество встроенных функций, которые может вызывать ваша программа. Например, strcat() для объединения двух строк, memcpy() для копирования одной ячейки памяти в другую и многие другие функции.
Функция также может называться методом, подпрограммой, процедурой и т. д.
Определение функции
Общая форма определения функции на языке программирования C выглядит следующим образом:
Определение функции в программировании на C состоит из заголовка функции и тела функции. Вот все части функции —
Тип возвращаемого значения — функция может возвращать значение. return_type — это тип данных значения, которое возвращает функция. Некоторые функции выполняют нужные операции, не возвращая значения. В этом случае return_type — это ключевое слово void.
Имя функции — это фактическое имя функции. Имя функции и список параметров вместе составляют сигнатуру функции.
Параметры — параметр подобен заполнителю. Когда функция вызывается, вы передаете значение параметру. Это значение называется фактическим параметром или аргументом. Список параметров относится к типу, порядку и количеству параметров функции. Параметры являются необязательными; то есть функция может не содержать параметров.
Тело функции. Тело функции содержит набор операторов, определяющих, что делает функция.
Пример
Ниже приведен исходный код функции max(). Эта функция принимает два параметра num1 и num2 и возвращает максимальное значение между ними —
Объявления функций
Объявление функции сообщает компилятору об имени функции и способе ее вызова. Фактическое тело функции можно определить отдельно.
Объявление функции состоит из следующих частей —
Для определенной выше функции max() объявление функции выглядит следующим образом: -
Имена параметров не важны в объявлении функции, требуется только их тип, поэтому следующее объявление также является допустимым —
Объявление функции требуется, когда вы определяете функцию в одном исходном файле и вызываете эту функцию в другом файле. В таком случае вы должны объявить функцию в начале файла, вызывающего функцию.
Вызов функции
При создании функции C вы даете определение того, что эта функция должна делать. Чтобы использовать функцию, вам нужно будет вызвать эту функцию для выполнения определенной задачи.
Когда программа вызывает функцию, управление программой передается вызываемой функции. Вызываемая функция выполняет определенную задачу, и когда выполняется ее оператор return или когда достигается закрывающая фигурная скобка, завершающая функцию, она возвращает управление программой обратно в основную программу.
Чтобы вызвать функцию, вам просто нужно передать необходимые параметры вместе с именем функции, и если функция возвращает значение, вы можете сохранить возвращенное значение. Например —
Мы сохранили max() вместе с main() и скомпилировали исходный код. При запуске окончательного исполняемого файла это приведет к следующему результату —
Аргументы функции
Если функция должна использовать аргументы, она должна объявить переменные, которые принимают значения аргументов. Эти переменные называются формальными параметрами функции.
Формальные параметры ведут себя как другие локальные переменные внутри функции и создаются при входе в функцию и уничтожаются при выходе.
При вызове функции есть два способа передачи аргументов в функцию:
Этот метод копирует фактическое значение аргумента в формальный параметр функции. В этом случае изменения параметра внутри функции не влияют на аргумент.
Этот метод копирует адрес аргумента в формальный параметр.Внутри функции адрес используется для доступа к фактическому аргументу, используемому в вызове. Это означает, что изменения, внесенные в параметр, влияют на аргумент.
По умолчанию C использует вызов по значению для передачи аргументов. В общем случае это означает, что код внутри функции не может изменять аргументы, используемые для вызова функции.
Функция – это набор операторов, которые принимают входные данные, выполняют определенные вычисления и производят выходные данные.
Идея состоит в том, чтобы объединить некоторые часто или многократно выполняемые задачи и создать функцию, чтобы вместо того, чтобы снова и снова писать один и тот же код для разных входных данных, мы могли вызвать функцию.
Общая форма функции:
Пример:
Ниже представлена простая программа на C/C++ для демонстрации функций.
- Функции помогают нам уменьшить избыточность кода. Если функциональность выполняется в нескольких местах программного обеспечения, то вместо того, чтобы снова и снова писать один и тот же код, мы создаем функцию и вызываем ее везде. Это также помогает в обслуживании, так как мы должны измениться в одном месте, если мы будем вносить изменения в функциональность в будущем.
- Функции делают код модульным. Рассмотрим большой файл с большим количеством строк кода. Читать и использовать код становится действительно просто, если код разделен на функции.
- Функции обеспечивают абстракцию. Например, мы можем использовать библиотечные функции, не беспокоясь об их внутренней работе.
Объявление функции
Объявление функции сообщает компилятору о количестве принимаемых функцией параметров, типах данных параметров и возвращаемом типе функции. Помещать имена параметров в объявление функции необязательно в объявлении функции, но необходимо указать их в определении. Ниже приведены примеры объявлений функций. (имена параметров отсутствуют в объявлениях ниже)
Всегда рекомендуется объявлять функцию перед ее использованием (подробности см. здесь, здесь и здесь)
В C мы можем сделать и объявление, и определение в одном месте, как это сделано в приведенном выше примере программы.
C также позволяет объявлять и определять функции отдельно, это особенно необходимо в случае библиотечных функций. Функции библиотеки объявляются в файлах заголовков и определяются в файлах библиотек. Ниже приведен пример объявления.
Передача параметров функциям
Параметры, передаваемые функции, называются действительными параметрами. Например, в приведенной выше программе 10 и 20 являются фактическими параметрами.
Параметры, полученные функцией, называются формальными параметрами. Например, в приведенной выше программе x и y являются формальными параметрами.
Есть два наиболее популярных способа передачи параметров.
Передача по значению. В этом методе передачи параметров значения фактических параметров копируются в формальные параметры функции, а два типа параметров сохраняются в разных местах памяти. Таким образом, любые изменения, сделанные внутри функций, не отражаются в фактических параметрах вызывающей программы.
Передача по ссылке И фактические, и формальные параметры ссылаются на одно и то же местоположение, поэтому любые изменения, сделанные внутри функции, фактически отражаются в фактических параметрах вызывающего объекта.
Параметры всегда передаются по значению в C. Например. в приведенном ниже коде значение x не изменяется с помощью функции fun().
Читайте также: