Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BOOK_С_INTUIT.doc
Скачиваний:
9
Добавлен:
19.09.2019
Размер:
7.91 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 (not debugging – без отладки). Для этого перед заголовочным файлом 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: %s\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;

while (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 \"%s\"\n", str );

#else

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

Break to debugging.

#endif

} while (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 %s \"%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 %s \"%c\", его код %d %s", \

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

else

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

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 осуществить оптимизацию использования памяти, выделяемой под структуру.

Для решения примера используем справку системы Visual Studio 2010 (выберем HelpIndexLook for; впишем 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.

П римечание. Выравнивание полей структуры можно выполнить при настройке компилятора в MS Visual Studio 2010 (рис. 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), произведите установку параметров закладки Struct Alignment для всех возможных значений (1 Byte, 2 Bytes, 4 Bytes, 8 Bytes, 16 Bytes). Определите также значение выравнивания полей структуры по умолчанию (Default).

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