- •Предисловие
- •Библиографический список
- •Контрольные вопросы
- •Библиографический список
- •Тема 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(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
Сделайте так, чтобы признак простого или непростого числа передавался из макроса. Этот признак должен использоваться в функции 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; 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
Стихотворение запишите в текстовый файл с именем 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 \"%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
Внесите изменения в программу, чтобы условие о невозможности компиляции было реализовано без директивы #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 %c \"%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 %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
В программе примените только буквы латинского алфавита.
Для записи четности или нечетности введенного символа предусмотрите массив символов str1иstr2.
Произведите конкатенацию двух строк «comp» и «Х» с помощью операции##, где Х – номер компьютера, на котором выполняется лабораторная работа.
Пример 6.Написать программу формирования структуры с информацией о студенте (об учащемся) и с помощью директивы#pragmaосуществить оптимизацию использования памяти, выделяемой под структуру.
Для решения примера используем справку системы VisualStudio2010 (выберемHelpIndexLookfor; впишем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
Запишите в двоичные файлы созданные структуры, проведя их инициализацию. Определите и сравните размеры созданных бинарных файлов. Произведите также чтение данных из бинарных файлов с выводом результата на консоль.
В программе для аргумента pack()примите знаяения 1, 2, 4, 8, 16. Объясните результаты выполнения программы.
Размер массива типа charпримите равным 31, т. е.char name[31]. Объясните результат выполнения программы.
В качестве элементов структуры используйте следующие типы данных: float,short int. Объясните результат выполнения программы.
Используя меню ProjectProperties(Alt+F7), произведите установку параметров закладкиStructAlignmentдля всех возможных значений (1 Byte, 2 Bytes, 4 Bytes, 8 Bytes, 16 Bytes). Определите также значение выравнивания полей структуры по умолчанию (Default).