Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BOOK_С_INTUIT.docx
Скачиваний:
31
Добавлен:
11.02.2015
Размер:
6.34 Mб
Скачать

Директива #line

Директива #lineизменяет содержимое__LINE__и__FILE__, которые являются зарезервированными идентификаторами (макросами) в компиляторе. В первом из них содержится номер компилируемой в данный момент строки программного кода программы [3], а второй идентификатор – это строка, содержащая имя компилируемого исходного файла.

Директива #lineвыглядит следующим образом:

#line номер "имя_файла"

В определении директивы #lineобязательным является номер строки, относительно которой будет выполняться подсчет следующих строк. Второй параметр"имя_файла" является не обязательным. Если его не будет, то идентификатор __FILE__ будет содержать путь и имя программы. Если указать в качестве параметра новое имя файла, то__FILE__ будет содержать его.

Директива #lineв основном используется для отладки и специальных применений [3].

Операторы препроцессора #и##

Операторы #и## применяются в сочетании с директивой#define[3], предусмотрены для работы препроцессора в некоторых особых случаях [3; 5].

Оператор #, который обычно называют операторомпревращения в строку(stringize), трансформирует аргумент, перед которым стоит, в строку, заключенную в кавычки. Должен использоваться в макросах с аргументами, поскольку операнд после#ссылается на аргумент макроса [5].

Оператор ##, называемый операторомсклеивания(pasting), или конкатенации, конкатенирует две лексемы. Операция##должна иметь два операнда [5].

Директива#pragma

Директива #pragma– это определяемая реализацией директива, которая позволяет передавать компилятору различные инструкции [3; 4]. Возможности этой директивы следует изучать по документации по компилятору.

Предопределенные символические константы

В языке Сопределены пять встроенных, предопределенных имен макрокоманд (символических констант) [2–5] (табл. 19.1). Они начинаются и заканчиваются двумя символами подчеркивания.

Таблица 19.1

Предопределенные символические константы

Символическая

константа

Объяснение

__LINE__

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

__FILE__

По умолчанию возвращает в виде строки символов имя компилируемого исходного файла

__DATE__

Возвращает в виде строки символов дату (мм дд гг) начала компиляции текущего исходного файла

__TIME__

Возвращает в виде строки символов время (чч:мм:сс) начала компиляции текущего исходного файла

__STDC__

Возвращает целую константу 1, которая указывает на то, что данная реализация совместима со стандартом ANSI

Макрос подтвержденияassert

Макрос assert, определенный в заголовочном файлеassert.h, проверяет значение выражения [5]. Если оно равно 0 (ложное значение), тоassertраспечатывает (например, выводит на консоль) сообщение об ошибке и вызывает функциюabort()(из библиотекиstdlib.h), завершающую работу программы. Например, в программе переменнаяхдолжна принимать значения, не превышающие 10. Для проверки и подтверждения такого условия в программу можно включить строку

assert( x <= 10 );

Если при выполнении данного макроса переменная хокажется больше 10, то выдается сообщение, содержащее номер строки и имя файла (например,main.c), в котором нарушено условие, а выполнение программы при этом прерывается. Формат выводимого сообщения зависит от конкретной реализации системы программирования [3]. С помощьюassertможно производить отладку программы во многих ее местах. Когда программа будет отлажена, то действие данного макроса можно устранить с помощью символической константыNDEBUG(notdebugging– без отладки). Для этого перед заголовочным файломassert.hследует вставить строку

#define NDEBUG

ПРАКТИЧЕСКАЯ ЧАСТЬ

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

Программный код решения примера

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <conio.h>

// Макрос с формальными параметрами

#define SIMPLE(x, d, b) for(d = 2; d < x; d++) \

if (!(x%d)) b = 0; \

if (b) puts("\n It is the simple number"); \

else puts("\n It is not the simple number");

int main (void) {

int b = 1, d = 0, x = 13;

printf("\n Enter the natural number: ");

scanf("%d", &x);

// Макрос с действительными параметрами

SIMPLE(x, d, b);

printf("\n %5sTime: %c\n Version C: %d \n", "", \

__TIME__, __STDC__);

printf("\n ... Press any key: ");

getch();

return 0;

}

В первой строке программы включена символическая константа для исключения вывода предупреждения относительно функции scanf()в средеMS Visual Studio 2010. В программе показано применение макроса функции с тремя формальными параметрами и несколькими строками программного кода.

Результат выполнения программы приведен на рис. 19.1.

Рис. 19.1. Проверка введенного числа на простоту

Результат Version C: 1означает, что компилятор поддерживает стандартANSIС.

Задание1

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

  2. В программе предусмотрите вывод на консоль общего количества строк программного кода.

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

  4. Напишите макрос с формальными параметрами для проверки на четность целого числа, введенного с клавиатуры.

  5. Напишите макрос с формальными параметрами для обмена значениями двух переменных (типа функции swap).

  6. Напишите макрос с формальными параметрами по вычислению площади круга по известному радиусу (вводимого с клавиатуры). Значение числа задайте с 15 знаками после десятичной точки при использовании директивы#define.

  7. Введите в программу вычисление случайных чисел, равномерно распределенных в интервале [0, Х], где Х – номер компьютера, на котором выполняется лабораторная работа. Количество случайных чисел должно соответствовать числу секунд (не равных нулю), определяемых с помощью символической константы __TIME__.

Пример 2.Выполнить проверку подключаемого тестового файла и вывести на консоль его содержимого. Содержимое файла – стихотворный пример бесконечной рекурсии: у попа была собака.

Программный код решения примера

// Файл с главной функцией main()

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <locale.h>

// Подключение текстового файла

#ifndef AZA

#define AZA

#include "dog.txt"

#endif

int main (void) {

short i, j, n, in;

i = j = 0;

setlocale (LC_ALL, "rus");

printf("\n Введите количество стихотворных строф: ");

in = scanf("%hd", &n);

if (in != 1 || n < 1) {

printf("\n Ошибка ввода данных. Нажмите любую клавишу: ");

getch();

exit(1);

}

// Условие распечатки текстового файла

#ifdef AZA

puts("");

for (j = 0; j < n; j++ )

{

i = 0;

if (d[i] != NULL)

{

printf(" ");

puts(d[i]);

i++;

}

}

#endif

printf("\n ... Нажмите любую клавишу: ");

getch();

return 0;

}

Содержимое текстового файла dog.txt:

char**d[] = {

"У попа была собака,", \

"Он её любил,", \

"Она съела кусок мяса,", \

"Он её убил...",\

"Вырыл ямку, закопал,", \

"На дощечке написал:\n"

};

Решение примера выполнено в виде двухфайлового проекта. Инициализация переменных в главной функции сделана на случай, если не будет определена директива #define AZA, чтобы не было предупреждений компилятора о неиспользованных переменныхiиj.

Пример выполнения программы показан на рис. 19.2.

Рис. 19.2. Пример распечатки текстового файла

Задание2

  1. Стихотворение запишите в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.

  2. Вместо препроцессорной директивы #ifdefпримените другую директиву условной компиляции.

  3. В программу включите директиву #else.

  4. Подключите препроцессорную директиву для исключения из программы именованной константы AZA.

  5. Создайте файл dog.hс содержимым файлаdog.txtи подключите его к проекту вместо файлаdog.txt.

  6. Напишите «чистую» рекурсивную функцию для распечатки стихотворения о попе и его собаке. В качестве аргумента функции включите количество стихотворных строф. Подсчитайте количество рекурсивных вызовов.

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

Программный код решения примера

#include <stdio.h>

#include <conio.h>

int main (void) {

char str[80];

// Начало проверки компилируемого кода

#ifdef _DEBUG

printf("\n Start debugging\n");

#endif

do {

printf("\n Enter a word or \"z\" to exit: ");

gets_s(str, 79);

#if _DEBUG

printf("\n The word is \"%c\"\n", str );

#else

#error This version is not to the C Run-Time Library. \

Break to debugging.

#endif

} if (str[0] != 'z' && str[0] != 'Z');

printf("\n\n ... Press any key: ");

getch();

return 0;

}

Символическая константа _DEBUGбудет определяться (существовать) в режимеDebug, который находится в списке главного меню интегрированной системыMS Visual Studio 2010. На рис. 19.3 представлен выбор режима отладкиDebug.

Рис. 19.3. Выбор режима отладки Debug

Пример выполнения программы показан на рис. 19.4.

Рис. 19.4. Пример выполнения режима отладки программы

Задание3

  1. Внесите изменения в программу, чтобы условие о невозможности компиляции было реализовано без директивы #error.

  2. Вместо специализированной константы _DEBUGвведите собственную символическую константуCOMP_X, где Х – номер компьютера, на котором выполняется лабораторная работа.

  3. Напишите программу со стеком, в который будут помещаться вводимые слова, а после предусмотрите возможность извлечения набранных слов (по дисциплине LIFO).

Пример 4.С помощью директивы#defineи оператора препроцессора# написать программу определения кода вводимого символа.

Программный код решения примера

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <conio.h>

#include <locale.h>

#define CHAR_COD(c) ""#c""

int main (void) {

char ch;

setlocale (LC_ALL, "rus");

printf("\n Введите символ: ");

scanf("%c", &ch);

printf("\n %c \"%c\", его код %d", CHAR_COD(Символ), ch, ch);

printf("\n\n ... Нажмите любую клавишу: ");

getch();

return 0;

}

Оператор #должен использоваться в макросах с аргументами, поскольку операнд ссылается на аргумент макроса [5]. В данном случае строка «Символ» подставляется в заменяющий текст вместо#c.

Пример выполнения программы показан на рис. 19.5.

Рис. 19.5. Пример определения кода вводимого символа

Задание4

  1. Напишите программу повторного ввода символов с клавиатуры до тех пор, пока не будет введен символ z.

  2. Выполните макроподстановку со строкой «compX» и присоединяемой строкой «Х» с помощью функцииprintf(), где Х – номер компьютера, на котором выполняется лабораторная работа.

Пример 5.С помощью директивы#defineи оператора препроцессора## классифицировать четность или нечетность кода вводимого символа.

Программный код решения примера

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <conio.h>

#include <locale.h>

// LEXEME - лексема

#define TWO_LEXEME(a,b) a ## b

#define CHAR_COD(c) ""#c""

int main (void) {

char ch;

setlocale (LC_ALL, "rus");

printf("\n Введите символ: ");

scanf("%c", &ch);

if (!(ch % 2))

printf("\n %c \"%c\", его код %d %c", \

CHAR_COD(Символ), ch, ch, TWO_LEXEME(" - четный","!"));

else

printf("\n %c \"%c\", его код %d %c", \

CHAR_COD(Символ), ch, ch, TWO_LEXEME(" - не ", "четный."));

printf("\n\n ... Нажмите любую клавишу: ");

getch();

return 0;

}

Данная программа – некоторое расширение предыдущей программы. Операция конкатенации строк ## осуществляется с помощью макросаTWO_LEXEME()с двумя аргументами.

Результат выполнения программы приведен на рис. 19.6.

Рис. 19.6. Пример классификации четности символа

Задание5

  1. В программе примените только буквы латинского алфавита.

  2. Для записи четности или нечетности введенного символа предусмотрите массив символов str1иstr2.

  3. Произведите конкатенацию двух строк «comp» и «Х» с помощью операции##, где Х – номер компьютера, на котором выполняется лабораторная работа.

Пример 6.Написать программу формирования структуры с информацией о студенте (об учащемся) и с помощью директивы#pragmaосуществить оптимизацию использования памяти, выделяемой под структуру.

Для решения примера используем справку системы VisualStudio2010 (выберемHelpIndexLookfor; впишемpragma, затем в правой части панели обратиимся к опцииpack). Опцияpackопределяет, как компилятор выравнивает данные при сохранении в памяти.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#pragma pack (show)

struct Student

{

char name[21]; // имя, фамилия

int age; // возраст, полных лет

char gender; // пол, мужской, женский

double mean; // средний балл успеваемости

} st1;

#pragma pack (push)

#pragma pack (1)

#pragma pack (show)

struct Student_pack

{

char name[21]; // имя, фамилия

int age; // возраст, полных лет

char gender; // пол, мужской, женский

double mean; // средний балл успеваемости

} st2;

#pragma pack (pop)

#pragma pack (show)

int main (void)

{

puts("");

printf (" sizeof (struct st1) = %d\n", sizeof (st1));

printf (" sizeof (struct st2) = %d\n", sizeof (st2));

printf("\n ... Press any key: ");

getch();

return 0;

}

В программе опция pack(show)используется для диагностики текущей упаковки полей структуры,pack(1) – используется для выравнивания областей памяти полей структуры, кратных единице,pack(push) – для «вталкивания» параметров. Опцияpack(pop)используется для «выталкивания» параметров.

После компиляции программы можно видеть сообщения, представленные на рис. 19.7.

Рис. 19.7. Сообщения об упаковке полей структуры

Предупреждения (warning) уведомляют о размере упаковки. Сначала область памяти выделяется под размер в байтах, кратных 8, затем, кратных 1. После выталкивания параметров снова область памяти становится кратной 8 – наибольшему типу данныхdoubleэлементаmeanструктуры.

Результат выполнения программы показан на рис. 19.8.

Рис. 19.8. Пример определения месяца года

Как видно, размер первой структуры равен 40 байтам (кратных 8), размер второй структуры, для которой произведена упаковка, – 34, что соответствует сумме размеров полей структуры: 34 = 21 + 4 + 1 + 8.

Примечание. Выравнивание полей структуры можно выполнить при настройке компилятора вMSVisualStudio2010 (рис. 19.9).

Рис. 19.9. Настройка компилятора (Code Generation  Struct Alignment)

Задание6

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

  2. В программе для аргумента pack()примите знаяения 1, 2, 4, 8, 16. Объясните результаты выполнения программы.

  1. Размер массива типа charпримите равным 31, т. е.char name[31]. Объясните результат выполнения программы.

  2. В качестве элементов структуры используйте следующие типы данных: float,short int. Объясните результат выполнения программы.

  3. Используя меню ProjectProperties(Alt+F7), произведите установку параметров закладкиStructAlignmentдля всех возможных значений (1 Byte, 2 Bytes, 4 Bytes, 8 Bytes, 16 Bytes). Определите также значение выравнивания полей структуры по умолчанию (Default).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]