Как создать основной файл c

Обновлено: 21.11.2024

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

Для создания файла в программе на языке C используется следующий синтаксис,

В приведенном выше синтаксисе файл представляет собой структуру данных, определенную в стандартной библиотеке.

fopen — это стандартная функция, которая используется для открытия файла.

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

fp — это указатель файла, указывающий на тип файла.

Каждый раз, когда вы открываете или создаете файл, вы должны указать, что вы собираетесь делать с файлом. Файл в программировании на языке C может быть создан или открыт для чтения/записи. Режим используется, чтобы указать, хотите ли вы открыть файл для какой-либо из указанных ниже целей. Ниже приведены различные типы режимов программирования на языке C, которые можно использовать при работе с файлом.

Режим файла Описание
r Открыть файл для чтения. Если файл находится в режиме чтения, то никакие данные не удаляются, если файл уже присутствует в системе.
w Открыть файл для записи . Если файл находится в режиме записи, то создается новый файл, если файл вообще не существует. Если файл уже присутствует в системе, то все данные внутри файла усекаются, и он открывается для записи.
a Откройте файл в
режиме добавления. Если файл находится в режиме добавления, то файл открывается. Содержимое файла не меняется.
r+ открыть для чтения и записи с начала
w+ открыть для чтения и записи, перезаписать файл
a+ открыть для чтения и записи, дополнить файл

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

Файл создается в той же папке, в которой вы сохранили свой код.

Вы можете указать путь, по которому вы хотите создать файл

Как закрыть файл

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

«C» предоставляет функцию fclose для выполнения операции закрытия файла. Синтаксис fclose следующий:

Функция fclose принимает в качестве аргумента указатель файла. Затем файл, связанный с указателем файла, закрывается с помощью функции fclose. Возвращает 0, если закрытие прошло успешно, и EOF (конец файла), если при закрытии файла произошла ошибка.

После закрытия файла тот же указатель файла можно использовать и с другими файлами.

В программировании на языке C файлы автоматически закрываются при завершении программы. Закрытие файла вручную путем написания функции fclose является хорошей практикой программирования.

Запись в файл

В C при записи в файл символы новой строки "\n" должны добавляться явно.

Библиотека stdio предлагает необходимые функции для записи в файл:

  • fputc(char, file_pointer): записывает символ в файл, на который указывает file_pointer.
  • fputs(str, file_pointer): записывает строку в файл, на который указывает file_pointer.
  • fprintf(file_pointer, str, variable_lists): печатает строку в файл, на который указывает file_pointer. Строка может дополнительно включать спецификаторы формата и список переменных variable_lists.

Приведенная ниже программа показывает, как выполнить запись в файл:

Функция fputc():

Приведенная выше программа записывает один символ в файл fputc_test.txt, пока не дойдет до символа следующей строки «\n», который указывает на то, что предложение было успешно записано. Процесс заключается в том, чтобы взять каждый символ массива и записать его в файл.

  1. В приведенной выше программе мы создали и открыли файл с именем fputc_test.txt в режиме записи и объявили нашу строку, которая будет записана в файл.
  2. Мы выполняем посимвольную операцию записи, используя цикл for, и помещаем каждый символ в наш файл до тех пор, пока не встретится символ «\n», после чего файл закрывается с помощью функции fclose.

fputs() Функция:

  1. В приведенной выше программе мы создали и открыли файл с именем fputs_test.txt в режиме записи.
  2. После того, как мы выполним операцию записи с помощью функции fputs(), записав три разные строки
  3. Затем файл закрывается с помощью функции fclose.

Функция fprintf():

  1. В приведенной выше программе мы создали и открыли файл с именем fprintf_test.txt в режиме записи.
  2. После выполнения операции записи с помощью функции fprintf() путем записи строки файл закрывается с помощью функции fclose.

Чтение данных из файла

Есть три разные функции, предназначенные для чтения данных из файла

  • fgetc(file_pointer): возвращает следующий символ из файла, на который указывает указатель файла. Когда конец файла достигнут, EOF отправляется обратно.
  • fgets(buffer, n, file_pointer): считывает n-1 символов из файла и сохраняет строку в буфере, к которому в качестве последнего добавляется символ NULL «\0».
  • fscanf(указатель_файла, спецификаторы_переменных, адреса_переменных): используется для разбора и анализа данных. Он считывает символы из файла и присваивает ввод списку указателей переменных variable_addresses, используя спецификаторы преобразования. Имейте в виду, что, как и в случае с scanf, fscanf прекращает чтение строки при обнаружении пробела или новой строки.

Следующая программа демонстрирует чтение из файла fputs_test.txt с использованием функций fgets(),fscanf() и fgetc() соответственно:

Результат:

  1. В приведенной выше программе мы открыли файл с именем «fprintf_test.txt», который был ранее написан с использованием функции fprintf() и содержит строку «Изучение C с Guru99». Мы читаем его с помощью функции fgets(), которая считывает строку за строкой, при этом размер буфера должен быть достаточным для обработки всей строки.
  2. Мы снова открываем файл, чтобы сбросить файл указателя на начало файла. Создайте различные строковые переменные для обработки каждого слова отдельно. Распечатайте переменные, чтобы увидеть их содержимое. Функция fscanf() в основном используется для извлечения и анализа данных из файла.
  3. Повторно откройте файл, чтобы сбросить файл указателя на начало файла. Считайте данные и распечатайте их из файла символ за символом, используя функцию getc(), пока не встретится оператор EOF
  4. После выполнения операции чтения файла с использованием разных вариантов мы снова закрыли файл с помощью функции fclose.

Интерактивное чтение и запись файлов с помощью getc и putc

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

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

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

Простой пример

Давайте начнем со следующих трех файлов, hellomake.c, hellofunc.c и hellomake.h, которые представляют собой типичную основную программу, некоторый функциональный код в отдельном файле и включаемый файл соответственно.

Обычно этот набор кода компилируется с помощью следующей команды:

gcc -o hellomake hellomake.c hellofunc.c -I.

При этом компилируются два файла .c и присваивается имя исполняемому файлу hellomake. -I. включен, так что gcc будет искать в текущем каталоге (.) включаемый файл hellomake.h. Без make-файла типичный подход к циклу тестирования/изменения/отладки заключается в использовании стрелки вверх в терминале для возврата к последней команде компиляции, чтобы вам не приходилось вводить ее каждый раз, особенно после добавления еще несколько файлов .c.

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

Самый простой make-файл, который вы можете создать, будет выглядеть примерно так:

Очень важно отметить, что перед командой gcc в make-файле есть вкладка. В начале любой команды должна быть вкладка, и make не будет счастлива, если ее там нет.

Для большей эффективности попробуем следующее:

Итак, мы определили некоторые константы CC и CFLAGS. Оказывается, это специальные константы, которые сообщают make, как мы хотим скомпилировать файлы hellomake.c и hellofunc.c. В частности, макрос CC — это используемый компилятор C, а CFLAGS — список флагов, которые необходимо передать команде компиляции. Помещая объектные файлы - hellomake.o и hellofunc.o - в список зависимостей и в правило, make знает, что сначала нужно скомпилировать версии .c по отдельности, а затем собрать исполняемый файл hellomake. .

Использование этой формы make-файла достаточно для большинства небольших проектов. Однако не хватает одной вещи: зависимости от включаемых файлов. Например, если бы вы внесли изменения в hellomake.h, make не стал бы перекомпилировать файлы .c, даже если это необходимо. Чтобы исправить это, нам нужно указать make, что все файлы .c зависят от определенных файлов .h. Мы можем сделать это, написав простое правило и добавив его в make-файл.

Это дополнение сначала создает макрос DEPS, представляющий собой набор файлов .h, от которых зависят файлы .c. Затем мы определяем правило, которое применяется ко всем файлам, оканчивающимся на суффикс .o. Правило гласит, что файл .o зависит от версии файла .c и файлов .h, включенных в макрос DEPS. Затем правило говорит, что для создания файла .o программе make необходимо скомпилировать файл .c с помощью компилятора, определенного в макросе CC. Флаг -c указывает на создание объектного файла, -o $@ указывает на размещение результатов компиляции в файле, указанном слева от :, $ является первым элементом в списке зависимостей, а макрос CFLAGS определен, как указано выше.

В качестве окончательного упрощения давайте воспользуемся специальными макросами $@ и $^, которые являются левой и правой сторонами : , соответственно, чтобы сделать общее правило компиляции более общим. В приведенном ниже примере все включаемые файлы должны быть перечислены как часть макроса DEPS, а все объектные файлы должны быть перечислены как часть макроса OBJ.

Что, если мы хотим начать размещать наши файлы .h в каталоге include, наш исходный код в каталоге src и некоторые локальные библиотеки в каталоге lib? Кроме того, можем ли мы как-то скрыть эти надоедливые файлы .o, которые висят повсюду? Ответ, конечно же, да. Следующий make-файл определяет пути к каталогам include и lib и помещает объектные файлы в подкаталог obj внутри каталога src. Он также имеет макрос, определенный для любых библиотек, которые вы хотите включить, таких как математическая библиотека -lm. Этот make-файл должен находиться в каталоге src. Обратите внимание, что оно также включает правило очистки ваших исходных файлов и каталогов объектов, если вы наберете сделать чистым. Правило .PHONY запрещает make делать что-либо с файлом с именем clean.

Итак, теперь у вас есть отличный make-файл, который вы можете модифицировать для управления небольшими и средними проектами программного обеспечения. Вы можете добавить несколько правил в make-файл; вы даже можете создавать правила, которые вызывают другие правила. Для получения дополнительной информации о make-файлах и функции make ознакомьтесь с Руководством по GNU Make, которое расскажет вам больше, чем вы когда-либо хотели знать (на самом деле).

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

Хотя перекомпилировать и скомпоновать программу с одним исходным кодом может быть проще с помощью одной команды cc, обычно удобнее компилировать программы с несколькими исходными кодами поэтапно — сначала путем компиляции каждого исходного файла в отдельный объект ( .o ), а затем путем связывания объектных файлов для формирования исполняемого файла ( a.out ). Этот метод требует больше места на диске, но последующие (повторяющиеся) перекомпиляции необходимо выполнять только для тех объектных файлов, для которых изменились исходники, что экономит время.

Простой пример Makefile

Следующий make-файл не выглядит элегантно, но работает.

Таблица 4-4 Простой Makefile для компиляции исходных кодов C: все явные

В этом примере make создает объектные файлы main.o и data.o , а также исполняемый файл functions :

Использование предопределенных макросов make

Следующий пример выполняет точно такую ​​же функцию, но демонстрирует использование предопределенных макросов make для указанных команд компиляции. Использование предопределенных макросов устраняет необходимость редактирования make-файлов при изменении базовой среды компиляции.Макросы также предоставляют доступ к макросу CFLAGS (и другим макросам FLAGS) для предоставления параметров компилятора из командной строки. Предопределенные макросы также широко используются в неявных правилах make. Предопределенные макросы в следующем make-файле перечислены ниже. [Предопределенные макросы используются более широко, чем в более ранних версиях make. Не все предопределенные макросы, показанные здесь, доступны в более ранних версиях. ] Как правило, они полезны для компиляции программ на C.

Командная строка cc; состоит из следующих значений CC, CFLAGS и CPPFLAGS, а также параметра -c.

Корень имени макроса, COMPILE , является соглашением, используемым для обозначения того, что макрос представляет собой командную строку компиляции (для создания объекта или файла .o). Суффикс .c — это мнемонический прием, указывающий, что командная строка применяется к файлам .c (исходный код C).

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

Базовая командная строка cc для связывания объектных файлов, например COMPILE.c , но без параметра -c и со ссылкой на макрос LDFLAGS:

Значение cc. (Вы можете переопределить это значение как путь к альтернативному компилятору C.)

Параметры команды cc; по умолчанию нет.

Параметры cpp ; по умолчанию нет.

Параметры редактора ссылок, ld ; по умолчанию нет.

Таблица 4-5 Makefile для компиляции исходных текстов C с использованием предопределенных макросов

Использование неявных правил для упрощения Makefile: правила суффиксов

Поскольку командные строки для компиляции main.o и data.o из их файлов .c теперь функционально эквивалентны правилу суффикса .c.o, их целевые записи являются избыточными; make выполняет одну и ту же компиляцию вне зависимости от того, присутствуют они в make-файле или нет. В следующей версии make-файла они устранены, полагаясь на правило .c.o для компиляции отдельных объектных файлов.

Таблица 4-6 Makefile для компиляции исходных кодов C с использованием суффиксных правил

Полный список правил суффиксов приведен в таблице 4-8.

Поскольку make обрабатывает зависимости main.o и data.o , он не находит для них целевых записей. Он проверяет применимость подходящего неявного правила. В этом случае make выбирает правило .c.o для создания файла .o из файла зависимостей с тем же базовым именем и суффиксом .c.

make использует порядок появления в списке суффиксов, чтобы определить, какой файл зависимостей и правило суффикса использовать. Например, если в каталоге есть файлы main.c и main.s, make использует правило .c.o, так как .c стоит впереди .s в списке.

Сначала make просматривает свой список суффиксов, чтобы увидеть, появляется ли суффикс для целевого файла. В случае с main.o в списке появляется .o. Затем проверьте наличие суффиксного правила для его сборки и файла зависимостей для его построения. Файл зависимостей имеет то же базовое имя, что и целевой, но другой суффикс. В этом случае при проверке правила .c.o make находит файл зависимостей с именем main.c и использует это правило.

Список суффиксов представляет собой цель специальной функции с именем .SUFFIXES. Различные суффиксы включены в определение макроса SUFFIXES; список зависимостей для .SUFFIXES указан как ссылка на этот макрос:

Таблица 4-7. Список стандартных суффиксов "top">СУФФИКСЫ= .o .c .c~ .cc .cc~ .C .C~ .y .y~ .l .l~ .s .s~ .sh .sh~ .S .S ~ .ln \ .h .h~ .f .f~ .F .F~ .mod .mod~ .sym .def .def~ .p .p~ .r .r~ \ .cps .cps~ .Y . Y~ .L .L~ .SUFFIXES: $(SUFFIXES)

В следующем примере показан make-файл для компиляции всего набора исполняемых программ, каждая из которых имеет только один исходный файл. Каждый исполняемый файл должен быть создан из исходного файла с тем же базовым именем и добавленным суффиксом .c. Например, demo_1 собран из demo_1.c .

Как и clean, all — это целевое имя, используемое по соглашению. Он создает «все» цели в своем списке зависимостей. Обычно make и make all эквивалентны.

В этом случае make не находит суффикс, совпадающий ни с одной из целей (через demo_5). Таким образом, он обрабатывает каждый, как если бы он имел нулевой суффикс. Затем он ищет суффиксное правило и файл зависимостей с допустимым суффиксом. В случае demo_2 будет найден файл с именем demo_2.c . Поскольку существует целевая запись для правила .c вместе с соответствующим файлом .c, make использует это правило для сборки demo_2 из demo_2.c .

Чтобы предотвратить двусмысленность, когда цель с нулевым суффиксом имеет явную зависимость, make не создает ее с использованием правила суффикса.Этот make-файл

не выводит:

Когда использовать явные целевые записи и неявные правила

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

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

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

Неявные правила и динамические макросы

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

Поскольку они не определены явным образом в make-файле, принято документировать динамические макросы с присоединенным префиксом $-sign (другими словами, показывая ссылку на макрос).

Имя текущей цели.

Список зависимостей новее, чем цель.

Имя файла зависимостей, как если бы оно было выбрано make для использования с неявным правилом.

Основное имя текущей цели (имя цели без суффикса).

Для библиотек: имя обрабатываемого элемента. Дополнительную информацию см. в разделе "Создание библиотек объектов".

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

$ заменяется именем файла зависимостей (в данном случае файла .c) для текущей цели.

Макрос OUTPUT_OPTION по умолчанию имеет пустое значение. Хотя по функциям он похож на CFLAGS, он предоставляется как отдельный макрос, предназначенный для передачи аргумента параметру компилятора -o, чтобы принудительно вывести компилятор в файл с заданным именем.

$@ заменяется именем текущей цели.

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

Динамические макромодификаторы

Динамические макросы можно изменить, включив в ссылку F и D. Если обрабатываемая цель имеет форму пути, $(@F) указывает часть имени файла, а $(@D) указывает часть каталога. Если в имени цели нет символов /, то $(@D) присваивается символ точки (.) в качестве значения. Например, для цели с именем /tmp/test $(@D) имеет значение /tmp; $(@F) имеет значение test.

Динамические макросы и список зависимостей: отложенные ссылки на макросы

Динамические макросы назначаются при обработке всех без исключения целей. Их можно использовать в целевом правиле как есть или в списке зависимостей, добавив перед ссылкой дополнительный символ $. Ссылка, начинающаяся с $$, называется отложенной ссылкой на макрос. Например, запись:

можно использовать для получения x.o из x.o.BAK и т. д. для y.o и z.o .

Список зависимостей читается дважды

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

Строка $$ является ссылкой на предопределенный макрос ` $ '. Для удобства этот макрос имеет значение `$'; когда make разрешает ее при начальном чтении, строка $$@ разрешается в $@. При сканировании зависимостей, когда результирующая ссылка на макрос $@ имеет значение, динамически назначенное ей, make разрешает ссылку на это значение.

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

выдает следующие результаты.

Правила оцениваются один раз

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

Запрещено переходное замыкание для суффиксных правил

Для суффиксных правил транзитивное замыкание отсутствует. Если бы у вас было правило суффикса для построения, скажем, файла .Y из файла .X, а другое — для создания файла .Z из файла .Y, make не стал бы комбинировать их правила для создания файла .Z из файла . Х файл. Вы должны указать промежуточные шаги в качестве целей, хотя их записи могут иметь пустые правила:

В этом примере trans.Z строится из trans.Y, если он существует. Без появления trans.Y в качестве целевой записи make мог бы завершиться с ошибкой «не знаю, как собрать», так как не было бы файла зависимостей, который можно было бы использовать. Целевая запись для trans.Y гарантирует, что make попытается собрать его, если он устарел или отсутствует. Поскольку в make-файле нет правил, make будет использовать соответствующее неявное правило, которым в данном случае будет правило .X.Y. Если trans.X существует (или может быть получен из SCCS ), при необходимости выполните перестроение как trans.Y, так и trans.Z.

Добавление правил для суффиксов

Хотя make предоставляет вам ряд полезных правил суффиксов, вы также можете добавлять новые собственные. Однако при добавлении новых неявных правил следует отдавать предпочтение правилам сопоставления с образцом (см. «Правила сопоставления с образцом: альтернатива правилам суффиксов». Если вам не нужно писать неявные правила, совместимые с более ранними версиями make, вы можете пропустить оставшаяся часть этого раздела, в которой описывается традиционный метод добавления неявных правил в make-файлы (процедура добавления неявных правил приведена здесь для совместимости с предыдущими версиями make).

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

Во-вторых, вы должны добавить целевую запись для правила суффикса:

Makefile с этими записями можно использовать для форматирования исходных файлов документов, содержащих макросы ms (файлы .ms), в выходные файлы troff (файлы .tr):

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

Затем вы можете добавить еще одну запись, содержащую новые суффиксы, а затем ссылку на макрос SUFFIXES, как показано ниже.

Правила сопоставления с образцом: альтернатива правилам суффиксов

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

где tp и ts — необязательные префикс и суффикс в имени цели, dp и ds — (необязательный) префикс и суффикс в имени зависимости, а % — это подстановочный знак, обозначающий базовое имя, общее для обоих.

проверить правила сопоставления с образцом перед правилами суффикса. Хотя это позволяет переопределить стандартные неявные правила, делать это не рекомендуется.

Если для построения цели нет правила, выполните поиск правила сопоставления с образцом перед проверкой правила суффикса. Если make может использовать правило сопоставления с образцом, он это делает.

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

Правило сопоставления с шаблоном для форматирования исходного файла troff в выходной файл troff выглядит следующим образом:

Правила суффиксов по умолчанию и предопределенные макросы make

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

Я знаю, Python и JavaScript — это то, с помощью чего современные дети пишут все свои безумные «приложения». Но не спешите сбрасывать со счетов C — это способный и лаконичный язык, который может многое предложить.Если вам нужна скорость, написание на C может быть вашим ответом. Если вы ищете безопасность работы и возможность узнать, как выследить разыменование нулевого указателя, C также может быть вашим ответом! В этой статье я объясню, как структурировать файл C и написать основную функцию C, которая обрабатывает аргументы командной строки, как чемпион.

Я: заядлый системный программист Unix.

Вы: у кого-то есть редактор, компилятор C и немного свободного времени.

Скучная, но правильная программа на C

Программа на C начинается с функции main(), обычно хранящейся в файле с именем main.c.

Эта программа компилирует, но ничего не делает.

Правильно и скучно.

Основные функции уникальны

Функция main() — это первая функция в вашей программе, которая выполняется, когда она начинает выполняться, но это не первая выполняемая функция. Первой функцией является _start(), которая обычно предоставляется библиотекой времени выполнения C и автоматически подключается при компиляции вашей программы. Детали сильно зависят от операционной системы и набора инструментов компилятора, поэтому я сделаю вид, что не упомянул об этом.

Функция main() имеет два аргумента, которые традиционно называются argc и argv и возвращают целое число со знаком. Большинство сред Unix предполагают, что программы будут возвращать 0 (ноль) в случае успеха и -1 (отрицательная единица) в случае неудачи.

Аргумент Имя Описание
argc Количество аргументов Длина вектора аргументов
argv Вектор аргументов Массив указателей на символы

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

Вектор аргументов всегда содержит по крайней мере одну строку в первом индексе, argv[0], которая представляет собой полный путь к выполняемой программе.

Структура файла main.c

Когда я пишу файл main.c с нуля, он обычно имеет следующую структуру:

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

Еще одна вещь, о которой я не буду говорить, — это комментарии.

Вместо комментариев используйте понятные имена функций и переменных.

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

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

1. Включает

Первое, что я добавляю в файл main.c, — это включения, чтобы сделать множество стандартных функций и переменных библиотеки C доступными для моей программы. Стандартная библиотека C делает много вещей; изучите файлы заголовков в /usr/include, чтобы узнать, что он может сделать для вас.

Это минимальный набор глобальных включений, который я буду включать по умолчанию для следующих вещей:

2. Определяет

Сейчас это не имеет большого смысла, но в определении OPTSTR я укажу, какие переключатели командной строки порекомендует программа. Обратитесь к man-странице getopt(3), чтобы узнать, как OPTSTR повлияет на поведение getopt().

Определение USAGE_FMT представляет собой строку формата в стиле printf(), на которую ссылается функция Usage().

3. Внешние объявления

Внешнее объявление переносит это имя в пространство имен текущей единицы компиляции (она же "файл") и позволяет программе получить доступ к этой переменной. Здесь мы ввели определения для трех целочисленных переменных и указателя на символ. Переменные с префиксом opt используются функцией getopt(), а errno используется в качестве внеполосного канала связи стандартной библиотекой C для сообщения о том, почему функция могла завершиться ошибкой.

4. Определения типов

После внешних объявлений я предпочитаю объявлять определения типов для структур, объединений и перечислений. Именование typedef само по себе является религией; Я настоятельно предпочитаю суффикс _t, чтобы указать, что имя является типом. В этом примере я объявил options_t как структуру с четырьмя членами. C — язык программирования, нейтральный к пробелам, поэтому я использую пробелы для выравнивания имен полей в одном столбце. Мне просто нравится, как это выглядит. Для объявлений указателя я добавляю звездочку к имени, чтобы было понятно, что это указатель.

5. Объявления глобальных переменных

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

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

При написании функций, добавляя их после функции main(), а не перед ней, включайте сюда прототипы функций. Ранние компиляторы C использовали однопроходную стратегию, что означало, что каждый символ (имя переменной или функции), который вы использовали в своей программе, должен был быть объявлен до того, как вы его использовали. Современные компиляторы — это почти все многопроходные компиляторы, которые создают полную таблицу символов перед генерацией кода, поэтому использование прототипов функций не является строго обязательным. Однако иногда вы не можете выбрать, какой компилятор использовать в вашем коде, поэтому напишите прототипы функций и вперед.

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

7. Разбор командной строки

Хорошо, это много. Назначение функции main() состоит в том, чтобы собрать аргументы, предоставленные пользователем, выполнить минимальную проверку ввода, а затем передать собранные аргументы функциям, которые будут их использовать. В этом примере объявляется переменная параметров, инициализированная значениями по умолчанию, и анализируется командная строка, при необходимости обновляя параметры.

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

  • Инициализировать opterr значением 0, что запрещает getopt выдавать символ ?.
  • Используйте выход(EXIT_FAILURE); или выйти (EXIT_SUCCESS); в середине main().
  • /* NOTREACHED */ — директива lint, которая мне нравится.
  • Используйте return EXIT_SUCCESS; в конце функций, возвращающих int.
  • Явно приводить неявные преобразования типов.

Сигнатура командной строки для этой программы, если бы она была скомпилирована, выглядела бы примерно так:

Фактически, это то, что use() выдаст в stderr после компиляции.

8. Объявления функций

Наконец, я пишу нестандартные функции. В этом примере функция do_the_needful() принимает указатель на структуру options_t. Я проверяю, что указатель параметров не равен NULL, а затем продолжаю проверять входные и выходные элементы структуры. EXIT_FAILURE возвращает значение, если какой-либо из тестов завершается неудачей, и, устанавливая для внешней глобальной переменной errno обычный код ошибки, я сообщаю вызывающему объекту об общей причине. Вызывающий может использовать удобную функцию perror() для выдачи удобочитаемых сообщений об ошибках на основе значения errno.

Функции почти всегда должны каким-то образом подтверждать свои входные данные. Если полная проверка стоит дорого, попробуйте сделать ее один раз и обработайте проверенные данные как неизменяемые. Функция Usage() проверяет аргумент progname, используя условное присваивание в вызове fprintf(). Функция Usage() в любом случае завершится, поэтому я не беспокоюсь об установке errno или создании большого шума из-за использования правильного имени программы.

Большой класс ошибок, которых я пытаюсь избежать, — это разыменование указателя NULL. Это заставит операционную систему отправить специальный сигнал моему процессу под названием SYSSEGV, что приведет к неминуемой смерти. Последнее, что хотят видеть пользователи, — это сбой из-за SYSSEGV. Гораздо лучше поймать указатель NULL, чтобы выдавать более качественные сообщения об ошибках и корректно завершать работу программы.

Некоторые люди жалуются на наличие нескольких операторов return в теле функции. Они приводят аргументы о «непрерывности потока управления» и других вещах. Честно говоря, если что-то пойдет не так в середине функции, самое время вернуть условие ошибки. Написание множества вложенных операторов if только для одного возврата никогда не является «хорошей идеей».™

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

Подождите, вы сказали, что комментариев нет.

В функции do_the_needful() я написал особый тип комментария, предназначенный для использования в качестве заполнителя, а не для документирования кода:

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

Собираем все вместе

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

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

Visual C++ включает в себя компилятор C, который можно использовать для создания чего угодно: от базовых консольных программ до полноценных приложений Windows Desktop, мобильных приложений и многого другого.

В этом пошаговом руководстве показано, как создать простую программу на C в стиле "Hello, World" с помощью текстового редактора, а затем скомпилировать ее в командной строке. Если вы предпочитаете работать на C++ в командной строке, см. Пошаговое руководство. Компиляция собственной программы на C++ в командной строке. Если вы хотите попробовать интегрированную среду разработки Visual Studio вместо использования командной строки, см. раздел Пошаговое руководство. Работа с проектами и решениями (C++) или Использование интегрированной среды разработки Visual Studio для разработки настольных систем C++.

Предпосылки

Для выполнения этого пошагового руководства необходимо установить либо Visual Studio и дополнительные компоненты Visual C++, либо инструменты сборки для Visual Studio.

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

Версия Visual Studio Инструменты сборки для Visual Studio устанавливает только набор инструментов командной строки, компиляторы, инструменты и библиотеки, необходимые для сборки программ C и C++. Он идеально подходит для создания лабораторий или занятий в классе и относительно быстро устанавливается. Чтобы установить только набор инструментов командной строки, загрузите Инструменты сборки для Visual Studio со страницы загрузок Visual Studio и запустите программу установки. В установщике Visual Studio выберите рабочую нагрузку инструментов сборки C++ и нажмите "Установить".

Прежде чем вы сможете создать программу C или C++ в командной строке, вы должны убедиться, что инструменты установлены и что вы можете получить к ним доступ из командной строки. Visual C++ имеет сложные требования к среде командной строки для поиска инструментов, заголовков и библиотек, которые он использует. Вы не можете использовать Visual C++ в обычном окне командной строки без некоторой подготовки. Вам потребуется окно командной строки разработчика, которое представляет собой обычное окно командной строки, в котором установлены все необходимые переменные среды. К счастью, Visual C++ устанавливает ярлыки для запуска командных строк разработчика, в которых среда настроена для сборки из командной строки. К сожалению, названия ярлыков командной строки разработчика и их расположение различаются почти во всех версиях Visual C++ и в разных версиях Windows. Ваша первая задача пошагового руководства — найти правильный ярлык для использования.

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

Эти инструкции различаются в зависимости от используемой версии Visual Studio. Чтобы просмотреть документацию для предпочитаемой версии Visual Studio, используйте элемент управления выбора версии. Он находится в верхней части оглавления на этой странице.

Откройте командную строку разработчика в Visual Studio 2022

Если вы установили Visual Studio 2022 в Windows 10 или более поздней версии, откройте меню "Пуск" и выберите "Все приложения". Затем прокрутите вниз и откройте папку Visual Studio 2022 (не приложение Visual Studio 2022). Выберите Командная строка разработчика для VS 2022, чтобы открыть окно командной строки.

Откройте командную строку разработчика в Visual Studio 2019

Если вы установили Visual Studio 2019 в Windows 10 или более поздней версии, откройте меню "Пуск" и выберите "Все приложения". Затем прокрутите вниз и откройте папку Visual Studio 2019 (не приложение Visual Studio 2019). Выберите Командная строка разработчика для VS 2019, чтобы открыть окно командной строки.

Откройте командную строку разработчика в Visual Studio 2017

Если вы установили Visual Studio 2017 в Windows 10 или более поздней версии, откройте меню "Пуск" и выберите "Все приложения".Затем прокрутите вниз и откройте папку Visual Studio 2017 (не приложение Visual Studio 2017). Выберите Командная строка разработчика для VS 2017, чтобы открыть окно командной строки.

Откройте командную строку разработчика в Visual Studio 2015

Если вы установили Microsoft Visual C++ Build Tools 2015 в Windows 10 или более поздней версии, откройте меню "Пуск" и выберите "Все приложения". Затем прокрутите вниз и откройте папку Visual C++ Build Tools. Выберите Visual C++ 2015 x86 Native Tools Command Prompt, чтобы открыть окно командной строки.

Если вы используете другую версию Windows, найдите в меню "Пуск" или на начальной странице папку инструментов Visual Studio, содержащую ярлык командной строки разработчика. Вы также можете использовать функцию поиска Windows для поиска «командной строки разработчика» и выбрать ту, которая соответствует установленной версии Visual Studio. Используйте ярлык, чтобы открыть окно командной строки.

Затем убедитесь, что командная строка разработчика Visual C++ настроена правильно. В окне командной строки введите cl (или CL , регистр не имеет значения для имени компилятора, но имеет значение для параметров компилятора). Вывод должен выглядеть примерно так:

В зависимости от версии Visual C++ и установленных обновлений могут быть различия в текущем каталоге или номере версии. Если приведенный выше вывод похож на то, что вы видите, значит, вы готовы создавать программы C или C++ в командной строке.

Если вы получаете сообщение об ошибке, например "'cl' не распознается как внутренняя или внешняя команда, исполняемая программа или пакетный файл", ошибка C1034 или ошибка LNK1104 при запуске команды cl, то либо вы не используете командная строка разработчика, или что-то не так с вашей установкой Visual C++. Прежде чем продолжить, вы должны устранить эту проблему.

Если вы не можете найти ярлык командной строки разработчика или получаете сообщение об ошибке при вводе cl , возможно, у вас возникла проблема с установкой Visual C++. Если вы используете Visual Studio 2017 или более позднюю версию, попробуйте переустановить разработку рабочего стола с рабочей нагрузкой C++ в установщике Visual Studio. Дополнительные сведения см. в разделе Установка поддержки C++ в Visual Studio. Или переустановите инструменты сборки со страницы загрузок Visual Studio. Не переходите к следующему разделу, пока не сработает команда cl. Дополнительные сведения об установке и устранении неполадок Visual Studio см. в разделе Установка Visual Studio.

В зависимости от версии Windows на компьютере и конфигурации безопасности системы вам может потребоваться щелкнуть правой кнопкой мыши, чтобы открыть контекстное меню для ярлыка командной строки разработчика, а затем выбрать «Запуск от имени администратора», чтобы успешно собрать и запустить программу, которая вы создаете, следуя этому пошаговому руководству.

Создайте исходный файл C и скомпилируйте его в командной строке

В окне командной строки разработчика введите cd c:\, чтобы изменить текущий рабочий каталог на корень вашего диска C:. Затем введите md c:\hello, чтобы создать каталог, а затем введите cd c:\hello, чтобы перейти в этот каталог. Этот каталог будет содержать ваш исходный файл и скомпилированную программу.

Введите notepad hello.c в командной строке разработчика. В появившемся диалоговом окне предупреждения Блокнота выберите Да, чтобы создать новый файл hello.c в рабочем каталоге.

В Блокноте введите следующие строки кода:

В строке меню Блокнота выберите «Файл» > «Сохранить», чтобы сохранить hello.c в рабочем каталоге.

Вернитесь в окно командной строки разработчика. Введите dir в командной строке, чтобы просмотреть содержимое каталога c:\hello. Вы должны увидеть исходный файл hello.c в списке каталогов, который выглядит примерно так:

Даты и другие данные на вашем компьютере будут отличаться. Если вы не видите файл исходного кода hello.c , убедитесь, что вы перешли в созданный вами каталог c:\hello, и в Блокноте сделайте убедитесь, что вы сохранили исходный файл в этом каталоге. Также убедитесь, что вы сохранили исходный код с расширением имени файла .c, а не с расширением .txt.

Чтобы скомпилировать программу, введите cl hello.c в командной строке разработчика.

Вы можете увидеть имя исполняемой программы hello.exe в строках выходной информации, отображаемой компилятором:

Если вы получаете сообщение об ошибке, например "'cl' не распознается как внутренняя или внешняя команда, исполняемая программа или пакетный файл", ошибка C1034 или ошибка LNK1104, командная строка вашего разработчика настроена неправильно. Чтобы узнать, как решить эту проблему, вернитесь к разделу Открытие командной строки разработчика.

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

Чтобы запустить программу, введите hello в командной строке.

Программа отображает этот текст, а затем завершает работу:

Поздравляем, вы скомпилировали и запустили программу C с помощью командной строки.

Дальнейшие шаги

Этот пример "Hello, World" настолько прост, насколько это возможно для программы на C. Реальные программы имеют файлы заголовков и другие исходные файлы, связываются с библиотеками и выполняют полезную работу.

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

cl файл1.c файл2.c файл3.c

Компилятор выводит программу с именем file1.exe. Чтобы изменить имя на program1.exe, добавьте параметр компоновщика /out:

cl file1.c file2.c file3.c /link /out:program1.exe

Чтобы автоматически обнаруживать больше ошибок программирования, мы рекомендуем вам компилировать с параметром уровня предупреждения /W3 или /W4:

cl /W4 файл1.c файл2.c файл3.c /ссылка /out:program1.exe

Компилятор cl.exe имеет гораздо больше параметров, которые можно применять для сборки, оптимизации, отладки и анализа кода. Для быстрого списка введите cl /? в командной строке разработчика. Вы также можете скомпилировать и связать отдельно и применить параметры компоновщика в более сложных сценариях сборки. Дополнительные сведения о параметрах компилятора и компоновщика и их использовании см. в справочнике по сборке C/C++.

Вы можете использовать NMAKE и make-файлы или MSBuild и файлы проекта для настройки и сборки более сложных проектов в командной строке. Дополнительные сведения об использовании этих инструментов см. в справочнике NMAKE и MSBuild.

Языки C и C++ похожи, но не одинаковы. Компилятор Microsoft C/C++ (MSVC) использует основное правило, чтобы определить, какой язык использовать при компиляции кода. По умолчанию компилятор MSVC обрабатывает все файлы, заканчивающиеся на .c, как исходный код C, а все файлы, заканчивающиеся на .cpp, как исходный код C++. Чтобы заставить компилятор обрабатывать все файлы как C независимо от расширения имени файла, используйте параметр компилятора /TC.

MSVC совместим со стандартами ANSI C89 и ISO C99, но не соответствует им строго. В большинстве случаев переносимый код C будет компилироваться и работать, как и ожидалось. Компилятор обеспечивает дополнительную поддержку изменений в ISO C11/C17. Для компиляции с поддержкой C11/C17 используйте флаг компилятора /std:c11 или /std:c17 . Для поддержки C11/C17 требуется Windows SDK 10.0.20201.0 или более поздней версии. Рекомендуется Windows SDK 10.0.22000.0 или более поздней версии. Вы можете загрузить последнюю версию SDK со страницы Windows SDK. Дополнительные сведения и инструкции по установке и использованию этого пакета SDK для разработки C см. в статье Установка поддержки C11 и C17 в Visual Studio.

Некоторые библиотечные функции и имена функций POSIX не поддерживаются MSVC. Функции поддерживаются, но предпочтительные имена изменились. Дополнительные сведения см. в разделе Функции безопасности в CRT и предупреждение компилятора (уровень 3) C4996.

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