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

Вид материалаЛабораторная работа

Содержание


Лабораторная работа № 10
Практические задания
Подобный материал:
1   2   3   4   5   6







Лабораторная работа № 10


ПРАВИЛА ИСПОЛЬЗОВАНИЯ ДИРЕКТИВ

ПРЕПРОЦЕССОРА

Цель: Изучение основных правил использования директив препроцессора Си.


Общие сведения


В интегрированную среду подготовки программ на Си++ или в компилятор языка как обязательный компонент входит препроцессор. Назначение препроцессора - обработка исходного текста программы при ее компиляции. Препроцессорная обработка в соответствии с требованиями стандарта языка Си++ включает несколько стадий, выполняемых последовательно. Конкретная реализация транслятора может разъединять несколько стадий, но результат должен быть таким, как если бы они выполнялись последовательно:
  • Все системно зависимые обозначения (например, системно зависимый индикатор конца строки) перекодируются в стандартные коды;
  • Каждая пара из символов '\' и "конец строки" убираются, и тем самым следующая строка исходного файла присоединяется к строке, в которой находилась эта пара символов;
  • В тексте распознаются директивы препроцессора, а каждый комментарий заменяется одним символом пустого промежутка.
  • Выполняются директивы препроцессора и производятся макроподстановки;
  • ЕSC-последовательности в символьных константах и символьных строках, например, '\n' заменяются на их эквиваленты (на соответствующие числовые коды);
  • Смежные символьные строки конкатенируются, т. е. соединяются в одну строку.

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

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

Для управления препроцессором, т. е. для задания нужных действий, используются команды (директивы) препроцессора, каждая из которых помещается на отдельной строке и начинается с символа #. Определены следующие препроцессорные директивы: #defune, #include, #undef, #if, #ifdef, #ifndef, #else, #endif, #elif, #line, #error, #pragma, #.

Директива #define имеет несколько модификаций. Они предусматривают определение макросов или препроцессорных идентификаторов, каждому из которых ставится в соответствие некоторая символьная последовательность. В последующем тексте программы препроцессорные идентификаторы заменяются на заранее запланированные последовательности символов.

Директива #include позволяет включать в текст программы текст из выбранного файла.

Директива #undef отменяет действие команды #define, которая определила до этого имя препроцессорного идентификатора.

Директива #if и ее модификации #ifdef, #ifndef совместно с директивами #else, #endif, #elif позволяют организовать условную обработку текста программы. Условность состоит в том, что компилируется не весь текст, а только те его части, которые так или иначе выделены с помощью перечисленных директив.

Директива #line позволяет управлять нумерацией строк в файле с программой. Имя файла и начальный номер строки указываются непосредственно в директиве #line.

Директива #еrror позволяет задать текст диагностического сообщения, которое выводится при возникновении ошибок.

Директива #pragma вызывает действия, зависящие от реализации.

Директива # ничего не вызывает, т. к. является пустой директивой, т. е. не дает никакого эффекта и всегда игнорируется.

Рассмотрим возможности перечисленных команд при решении типичных задач, поручаемых препроцессору.

Для замены идентификатора заранее подготовленной последовательностью символов используется следующая директива (обратите внимание на пробелы): #define идентификатор строка замещения.

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


Исходный текст Результат препроцессорной обработки

#define begin {

#define end }

void main() void main()

begin {

операторы операторы

end }


В данном случае программист решил использовать в качестве операторных скобок идентификаторы begin, end.

Компилятор языка Си++ не может обрабатывать таких скобок, и поэтому до компиляции препроцессор заменяет все вхождения этих идентификаторов стандартными скобками { и }. Соответствующие указания программист дал препроцессору с помощью директив #define.

Директиву #undef удобно использовать при разработке больших программ, когда они собираются из отдельных "кусков текста", написанных в разное время или разными программистами. В этом случае могут встретиться одинаковые обозначения разных объектов. Чтобы не изменять исходных файлов, включаемый текст можно "обрамлять" подходящими директивами #define - #undef и тем самым устранять возможные ошибки. Приведем пример:




А = 10; // Основной текст



#define A X



А = 5; // Включенный текст



#undef A



В = А; // Основной текст




При выполнении программы В примет значение 10, несмотря на наличие оператора присваивания А = 5 во включенном тексте.

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


#include <имя файла> // Имя в угловых скобках

#include "имя файла" // Имя в кавычках.


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

Начиная работать с языком Си++, пользователь сразу же сталкивается с необходимостью использования в программах средств ввода-вывода. Для этого в начале текста программы помещают директиву #include

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

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

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


#include // Включение средств обмена

extern int ii, jj, 11; // Целые внешние переменные

extern float AA, BB; // Вещественные внешние переменные


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


Практические задания:

  1. Разработать функцию зануления столбца двумерного массива. Занулить 3 столбца. Результат вывести на печать.
  2. Разработать функцию зануления строки двумерного массива. Занулить 3 произвольные строки. Результат вывести на печать.
  3. Разработать функцию очистки (зануления) двумерного массива. Занулить два массива. Результат вывести на печать.
  4. Разработать функцию, позволяющую инвертировать каждый столбец двумерного массива целых чисел. Обработать два массива. Результат вывести на печать.
  5. Разработать функцию, которая меняет местами содержимое старшего и младшего байтов каждого элемента двумерного массива. Ввести два двумерных массива разной длины. Результаты вывести на печать.
  6. Заполнить 2 двумерных массива разной длины символами. Написать функцию, которая ставит символ пробела в старшем байте каждого слова массива. Обработать 2 массива. Результаты вывести на печать.
  7. Разработать функцию поэлементного суммирования двух строк массива в одну. Ввести массив. Сложить два раза по две строки. Вывести на печать.
  8. Разработать функцию поэлементного суммирования двух столбцов двумерного массива. Просуммировать два раза по два столбца. Результат вывести на печать.
  9. Разработать функцию, позволяющую поменять местами две произвольные столбца двумерного массива. Ввести массив. Поменять два раза по два столбца. Результат вывести на печать.
  10. Разработать функцию, позволяющую поменять местами две произвольные строки двухмерного массива. Ввести массив. Поменять два раза по две строки. Результат вывести на печать.
  11. Разработать функцию выделения произвольного столбца в двумерном массиве. Ввести массив и выделить в нем два произвольных столбца. Результат вывести на печать.
  12. Разработать функцию поэлементного умножения массива на константу. Результат вывести на печать.
  13. Разработать функцию поэлементного сложения двух двумерных массивов одинаковой длины в третий. Ввести шесть двумерных массивов. Результат вычислений вывести на печать.
  14. Разработать функцию выделения произвольной строки из двумерного массива. Ввести двумерный массив. Выделить и вывести две строки. Результат вывести на печать.
  15. Разработать функцию суммирования элементов двумерного массива произвольной длины. Ввести два массива разной длины. Результат вычислений вывести на печать.
  16. Разработать функцию, которая переводит десятичное число в двоичное. Ввести пять чисел. Результат вывести на печать.
  17. Разработать функцию перевода десятичного числа в шестнадцатеричное представление. Ввести пять чисел. Результат вывести на печать.
  18. Разработать функцию перевода двоичного числа в шестнадцатеричное. Ввести десять чисел. Результат вывести на печать.
  19. Разработать функцию перевода двоичного числа в восьмеричное представление. Ввести восемь чисел. Результат вывести на печать.
  20. Разработать функцию перевода десятичного числа в восьмеричное представление. Ввести десять чисел. Результат вывести на печать.





Лабораторная работа № 11