Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Практикум по программированию на языке Си

..pdf
Скачиваний:
25
Добавлен:
12.11.2023
Размер:
3.53 Mб
Скачать

!Символы входного потока, прочитанные функциями gets() или scanf() по спецификации %s, автоматически дополняются в "принимающем" символьном массиве признаком конца строки '\0' (09_06.c, 09_11.c и др.).

!Длину строки вычисляют как количество отличных от '\0' элементов от начала cодержащего строку массива char[] (функция sizeStr() в 09_08.c, библиотечная функция strlen()).

!При оформлении строки в массиве типа char[] НЕ ЗАБЫВАЙТЕ завершать содержательную часть строки кодом '\0' (09_10.с,

09_12.с).

!Обязательное наличие кода '\0' в конце строки позволяет использовать строки в качестве возвращаемого функцией значения. На самом деле функция возвращает указатель типа char *, но этого достаточно для доступа ко всей строке (09_11.с, 09_12_1.с).

!Применение библиотечных функций позволяет существенно упростить программы для работы со строками в стиле Си

(09_13.с и др.).

!Изучите возможности стандартных библиотечных функций для работы со строками и при составлении алгоритма решения задачи оперируйте в терминах действий, выполняемых функциями

(09_13.с, 09_13_1.с, 09_14.с и др.).

!Возвращаемая функцией строка должна быть сформирована внутри тела функции в массиве статистическом, динамическом либо глобальном (функция word() в 09_14.с, функция measure() в 09_16.с, 09_22.с и др.).

!Недопустимо возвращать из функции указатель на объект, сформированный в автоматической памяти тела функции.

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

(09_13.с, 09_18.с).

!Библиотечные функции для преобразования строчных букв в прописные и обратно предназначены только для латинского ал-

фавита (09_15_1.с).

!С помощью библиотечных функций можно получить строку с символьным представлением арифметического значения любого базового типа (09_16.с, 09_18.с).

401

!Библиотечные функции обеспечивают для строк все возможности форматных обменов, существующие для потоков ввода-

вывода (09_16.с, 09_18_1.с).

!Значения, указанные в командной строке при запуске программы, воспринимаются функцией main() как указатели на символьные строки, первая их которых содержит полное имя исполняемого модуля программы (09_17.с, 09_18.с).

!Рекурсия – основа обработки регулярных выражений (09_20.с).

!Массив указателей на строки позволяет адресовать строки разной длины (09_21.с, 09_22.с и др.).

!Массив указателей на строки – эффективное средство для сортировки строк по произвольным правилам (09_23.с, 09_24.с).

!При определении массив указателей на строки всегда должен получать память для своих элементов (09_21.с - 09_23.с).

!Указатель типа char ** при определении получает память только для самого себя (для указателя), даже если он адресует массив указателей (09_24.с).

Тема 10

Условная компиляция и макрообработка

Макрообработка – это, в сущности, замена одного текста другим.

М. Кэмпбел-Келли. Введение в макросы

Основные вопросы темы

!Препроцессорные директивы ветвлений обработки текста.

!Логические препроцессорные выражения.

!Схемы алгоритмов препроцессорной обработки.

!Препроцессорное задание размеров многомерных массивов.

!Препроцессорная настройка программ.

!Назначение и возможности препроцессорных операций.

!Препроцессорные макроопределения и макроподстановки.

!Различия и сходства макросов и функций.

!Моделирование многомерных массивов.

!Макроопределения шаблонов функций.

10.1.Условная генерация текста

Ранее (в теме 3) мы изучили препроцессорные средства и механизмы для управления выборкой фрагментов из обрабатываемого текста. В литературе о таком использовании препроцессора говорят, применяя термины "условная компиляция" и "условная генерация (текста)". В рассмотренных в теме 3 примерах использовались директивы #ifndef, #else, #endif. Управление выборкой частей текста осуществлялось за счет проверки препроцессорной определенности идентификатора. Однако проверяться могут и значения логических выражений, в которые входят константы и препроцессорные идентификаторы. Логические выражения используются в директиве #if.

403

ЗАДАЧА 10-01. Используя директивы условной препроцессорной обработки, оформите текстовый файл таким образом, чтобы текст из него можно было включить в одну программу не более двух раз.

/*10_01.txt - текст включается не более 2-х раз */ #ifndef _T

#define _T 1 #endif

#if _T<3

Значение препроцессорного идентификатора: _T #if _T==2

#undef _T #define _T 3

#endif #if _T==1

#undef _T #define _T 2

#endif

#endif

Вначале строка замещения "1" приписывается препроцессорному идентификатору _T директивой #define _T 1. Идентификатор определен со значением 1 только при первом просмотре. При выполнении условия _T<3 строка "Значение..." включается в текст, а затем проверяется условие _T==2 и т. д. (рис. 10.1). Обратите внимание, что значение строки замещения препроцессорного идентификатора (в нашем случае идентификатора _T) сохраняется до повторного обращения к тексту при его последующем включении директивой

#include. На рис. 10.1 конструкцией := обозначено установление связи директивой #define препроцессорного идентификатора со строкой замещения.

В следующей "программе", размещенной в файле 10_01.с, рассмотренный текст четыре раза включается директивой #include.

/* 10_01.c - защита от многократных включений */ #include "10_01.txt"

#include "10_01.txt" #include "10_01.txt" #include "10_01.txt"

404

Рис. 10.1.Схема препроцессорной обработки текста 10_01.txt

Программу из файла 10_01.с можно подвергнуть препроцессорной обработке (без последующей компиляции), используя директиву компилятора DJGPP:

>gcc –E 10_01.c>pre<ENTER>

В текстовый файл с указанным именем "pre" будет помещен такой результат препроцессорной обработки (удалены пустые строки):

405

#1 "10_01c"

#1 "10_01.txt" 1

Значение препроцессорного идентификатора: 1

#2 "10_01.c" 2

#1 "10_01.txt" 1

Значение препроцессорного идентификатора: 2

#3 "10_01.c" 2

#1 "10_01.txt" 1

#15 "10_01.txt"

#4 "00_10.c" 2

#1 "10_01.txt" 1

#15 "10_01.txt"

#5 "10_01.c" 2

Результат подтверждает правильность защиты, строка "значение препроцессорного идентификатора _T" печатается только дважды.

10.2.Рекурсивное использование директивы

#include

Известное английское стихотворение в переводе С.Я. Маршака "Дом, который построил Джек" состоит из 9 строф. При переходе от одной строфы к следующей первая строка заменяется двумя новыми, а остальные повторяются. Исключение: при переходе от первой строфы ко второй одна строка заменяется тремя.

ЗАДАЧА 10-02. С использованием препроцессорных директив "сконструировать" такой текст, который не содержал бы повторяющихся в стихотворении С.Я. Маршака строк, но при процессорной обработке которого все стихотворение "разворачивалось" бы полностью с нужным повторением строк.

Следующий текст решает поставленную задачу для первых четырех строф стихотворения С.Я. Маршака.

//dom.txt - Маршак С. "Дом, который построил Джек" ifndef _T

#define _T 1

406

#endif #if _T<5 #if _T==4

Вот кот, #undef _T

#define _T 5 #endif

#if _T==3

А это веселая птица-синица, #undef _T

#define _T 4 #endif

#if _T==2

А это пшеница, #undef _T #define _T 3

#endif #if _T==1

Вот дом, #undef _T

#define _T 2 #endif

#if _T>4

Который пугает и ловит синицу, #endif

#if _T>3

Которая часто ворует пшеницу, #endif

#if _T>2

Которая в темном чулане хранится В доме,

#endif

Который построил Джек. #include "dom.txt" #endif

"Программа", при обработке которой препроцессор формирует первые четыре строфы стихотворения С.Я. Маршака:

// 10_02.с - "разворачивание" стихотворения Маршака

#include "dom.txt"

407

Как и в предыдущей задаче, выполним препроцессорную обработку по команде

>gcc –E 10_02.c>pre<ENTER>

Результаты препроцессорной обработки в файле pre:

#1 "10_02.c"

#1 "dom.txt" 1

Вот дом, Который построил Джек.

#1 "dom.txt" 1

А это пшеница, Которая в темном чулане хранится В доме,

Который построил Джек.

# 1 "dom.txt" 1

А это веселая птица-синица, Которая часто ворует пшеницу, Которая в темном чулане хранится В доме, Который построил Джек.

#1 "dom.txt" 1

Вот кот, Который пугает и ловит синицу,

Которая часто ворует пшеницу, Которая в темном чулане хранится В доме, Который построил Джек.

#1 "dom.txt" 1

#46 "dom.txt"

#45 "dom.txt" 2

#45 "dom.txt" 2

#45 "dom.txt" 2

#45 "dom.txt" 2

#2 "10_02.c" 2

ЗАДАНИЕ. Нарисуйте схему алгоритма препроцессорной обработки, соответствующую тексту "свернутого" стихотворения С.Я. Маршака.

408

В решенной задаче текст из файла dom.txt превращается за счет препроцессорной обработки в стихотворение. Но кроме стихотворных строк в результат включена вспомогательная информация – строки, начинающиеся с символов '#'.

ЗАДАЧА 10-03. Преобразуйте текст из файла dom.txt таким образом, чтобы его включение в программу позволяло этой программе напечатать (вывести на экран дисплея) текст стихотворения Маршака без вспомогательных строк (начинающихся с символа '#').

Используем для решения функцию puts(), аргументом которой должна быть символьная строка. Строку будем формировать как конкатенацию размещенных рядом строковых констант. Для правильного расположения текста используем эскейп-последователь- ности кодов управляющих символов ('\n', '\t').

Текст свернутого представления стихотворения:

/*domPrint.txt - Маршак С. "Дом, который построил Джек" */

#ifndef _T #define _T 1

puts(

"\t\tМаршак С.\n\t\"Дом,который построил Джек\"\n"); #endif

#if _T<5 puts(

#if _T==4 "Вот кот,\n"

#undef _T #define _T 5

#endif #if _T==3

"А это веселая птица-синица,\n" #undef _T

#define _T 4 #endif

#if _T==2

"А это пшеница,\n" #undef _T #define _T 3

#endif

409

#if _T==1 "Вот дом,\n"

#undef _T #define _T 2

#endif #if _T>4

"Который пугает и ловит синицу,\n" #endif

#if _T>3

"Которая часто ворует пшеницу,\n" #endif

#if _T>2

"Которая в темном чулане хранится\n" "В доме,\n"

#endif

"Который построил Джек.\n" );

#include "domPrint.txt" #endif

Программа печати стихотворения из файла domPrint.txt:

/* 10_03.c - печать стихотворения Маршака */ #include <stdio.h>

int main()

{

#include "domPrint.txt" return 0;

}

Результат выполнения программы:

Маршак С.

"Дом, который построил Джек"

Вот дом, Который построил Джек.

А это пшеница, Которая в темном чулане хранится В доме,

Который построил Джек.

410