
- •Предисловие
- •Контрольные вопросы
- •Библиографический список
- •Тема 2 Переменные и базовые типы данных языка с
- •Контрольные вопросы
- •Библиографический список
- •Тема3 Организация циклов в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 4 Принятие решений. Условные операторы в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 5 Числовые массивы в языке программирования с
- •Тип имя_массива[размер];
- •Тип имя_массива[размер1] [размер2];
- •Тип имя_массива[размер1] [размер2] [размерN];
- •Контрольные вопросы
- •Библиографический список
- •Тема 6 Символьные массивы в языке с. Работа со строками
- •Тип имя_массива[размер];
- •Тип имя_массива[размер1] [размер2];
- •Тип имя_массива[размер1] [размер2] [размерN];
- •Контрольные вопросы
- •Библиографический список
- •Тема 7 Указатели в языке программирования с
- •Int *ptr; // объявили указатель на целую переменную
- •Контрольные вопросы
- •Библиографический список
- •Тема 8 Указатели и массивы в языке с
- •Int data[7]; // обычный массив
- •Int *pd[7]; // массив указателей
- •Контрольные вопросы
- •Библиографический список
- •Тема 9 Динамическое распределение памяти в языке с
- •If (!ptr) // условие логического отрицания
- •If (!ptr) // условие логического отрицания
- •Контрольные вопросы
- •Библиографический список
- •Тема 10 Функции Общие сведения о функциях языка с
- •Fun(тип имя_перем1, тип имя_перем2, , тип имя_перем n)
- •Контрольные вопросы
- •Библиографический список
- •Тема 11 Указатели и функции в языке программирования с
- •Тип_возвращаемый_функцией(*имя_указателя_на_функцию)(аргументы);
- •Контрольные вопросы
- •Библиографический список
- •Тема 12 Файловый ввод/вывод в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 13 Структуры – производные типы данных языка с
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Контрольные вопросы
- •Библиографический список
- •Тема 14 Объединения и перечислимые типы в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 15 Структуры и функции языка с
- •Контрольные вопросы
- •Библиографический список
- •Тема 16 Операции с разрядами (битами) в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 17 Программы, состоящие из нескольких файлов, на языке с
- •Спецификатор extern
- •Спецификатор static
- •Спецификатор register
- •Спецификатор auto
- •Контрольные вопросы
- •Библиографический список
- •Тема 18 Рекурсивные алгоритмы и функции
- •Переместить (a, b);
- •Контрольные вопросы
- •Библиографический список
- •Тема 19 Препроцессор языка с
- •Директива #define
- •Директива #error
- •Директива #include
- •Директивы условной компиляции
- •2_ Я_последовательность операторов программного кода
- •3_ Я_последовательность операторов программного кода
- •Директива #line
- •Директива #pragma
- •Предопределенные символические константы
- •Макрос подтверждения assert
- •Контрольные вопросы
- •Библиографический список
- •Тема 20 Программы на языке с при использовании статически подключаемой библиотеки
- •Контрольные вопросы
- •Библиографический список
- •Тема 21 Использование аргументов командной строки в с
- •Контрольные вопросы
- •Контрольная работа № 2 Покупки в супермаркете
- •Приложение Управление конфигурациями проекта в Visual Studio 2010
Директива #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
Сделайте так, чтобы признак простого или непростого числа передавался из макроса. Этот признак должен использоваться в функции main() для вывода соответствующего сообщения на консоль.
В программе предусмотрите вывод на консоль общего количества строк программного кода.
В программе предусмотрите вывод на консоль даты компиляции и имени компилируемого файла.
Напишите макрос с формальными параметрами для проверки на четность целого числа, введенного с клавиатуры.
Напишите макрос с формальными параметрами для обмена значениями двух переменных (типа функции swap).
Напишите макрос с формальными параметрами по вычислению площади круга по известному радиусу (вводимого с клавиатуры). Значение числа задайте с 15 знаками после десятичной точки при использовании директивы #define.
Введите в программу вычисление случайных чисел, равномерно распределенных в интервале [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
Стихотворение запишите в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.
Вместо препроцессорной директивы #ifdef примените другую директиву условной компиляции.
В программу включите директиву #else.
Подключите препроцессорную директиву для исключения из программы именованной константы AZA.
Создайте файл dog.h с содержимым файла dog.txt и подключите его к проекту вместо файла dog.txt.
Напишите «чистую» рекурсивную функцию для распечатки стихотворения о попе и его собаке. В качестве аргумента функции включите количество стихотворных строф. Подсчитайте количество рекурсивных вызовов.
Пример 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
Внесите изменения в программу, чтобы условие о невозможности компиляции было реализовано без директивы #error.
Вместо специализированной константы _DEBUG введите собственную символическую константу COMP_X, где Х – номер компьютера, на котором выполняется лабораторная работа.
Напишите программу со стеком, в который будут помещаться вводимые слова, а после предусмотрите возможность извлечения набранных слов (по дисциплине 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
Напишите программу повторного ввода символов с клавиатуры до тех пор, пока не будет введен символ z.
Выполните макроподстановку со строкой «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
В программе примените только буквы латинского алфавита.
Для записи четности или нечетности введенного символа предусмотрите массив символов str1 и str2.
Произведите конкатенацию двух строк «comp» и «Х» с помощью операции ##, где Х – номер компьютера, на котором выполняется лабораторная работа.
Пример 6. Написать программу формирования структуры с информацией о студенте (об учащемся) и с помощью директивы #pragma осуществить оптимизацию использования памяти, выделяемой под структуру.
Для решения примера используем справку системы Visual Studio 2010 (выберем HelpIndexLook 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
Запишите в двоичные файлы созданные структуры, проведя их инициализацию. Определите и сравните размеры созданных бинарных файлов. Произведите также чтение данных из бинарных файлов с выводом результата на консоль.
В программе для аргумента pack() примите знаяения 1, 2, 4, 8, 16. Объясните результаты выполнения программы.
Размер массива типа char примите равным 31, т. е. char name[31]. Объясните результат выполнения программы.
В качестве элементов структуры используйте следующие типы данных: float, short int. Объясните результат выполнения программы.
Используя меню Project Properties (Alt + F7), произведите установку параметров закладки Struct Alignment для всех возможных значений (1 Byte, 2 Bytes, 4 Bytes, 8 Bytes, 16 Bytes). Определите также значение выравнивания полей структуры по умолчанию (Default).