- •Министерство образования Республики Беларусь
- •1. Что такое программа на языке программирования
- •2. Общее знакомство с языком с
- •3. Структура простой программы на языке с
- •4. Что такое программа на языке с
- •5. Представление информации и типы данных в языке с
- •6. Константы
- •7. Переменные
- •8. Элементарный вВод и вывод информации
- •9. Выражения и операции
- •9.1. Арифметические операции
- •9.2. Операция изменения знака
- •9.3. Операции инкремента и декремента
- •9.4. Операция присваивания
- •9.6. Поразрядные логические операции
- •9.8. Логические операции и операции отношения
- •9.9. Условная операция «? :»
- •9.10. Операция последовательного вычисления
- •9.11. Операция определения требуемой памяти в байтах sizeof
- •9.12. Операция приведения типа (type)
- •10. Операторы управления вычислительным процессом
- •10.1. Операторы ветвления if и else
- •10.2. Оператор switch
- •10.3. Оператор цикла while
- •10.4. Оператор цикла do…while
- •10.5. Оператор цикла for
- •10.6. Бесконечные циклы
- •10.7. Другие управляющие средства языка с
- •10.8. Стандартные математические функции
- •11. Вычисление выражений и побочные эффекты
- •11.1. Преобразования типов при вычислении выражений
- •11.2. Побочные эффекты при вычислении выражений
- •12. Массивы
- •12.1. Описание массива
- •12.2. Инициализация массива
- •12.3. Ввод-вывод массива
- •12.4. Двумерные массивы (массивы массивов)
- •13. Указатели
- •14. Адресная арифметика
- •15. Массивы и указатели
- •15.1. Указатели и одномерные массивы
- •15.2. Указатели и двумерные массивы
- •16. Строки
- •17. Массивы строк
- •18. Функции
- •18.1. Определение функции в языке с
- •18.2. Возвращение значений из функции
- •18.3. Формальные и фактические параметры функции
- •18.4. Вызов функции
- •18.5. Объявление и определение функции: прототип функции
- •19. Передача параметров в функции
- •19.1. Способы передачи параметров в функции
- •19.2. Передача параметров в функции в языке с
- •19.3. Передача указателей в функции
- •20. Классы хранения и видимость переменных
- •20.1. Общие положения
- •20.2. Спецификаторы класса памяти
- •20.3. Область видимости функций
- •20.4. Глобальные переменные
- •20.5. Глобальные статические переменные
- •20.6. Локальные переменные
- •20.7. Статические локальные переменные
- •20.8. Регистровые переменные
- •20.9. Выводы
- •21. Организация памяти программы
- •22. Многофайловая компиляция (проекты)
- •23. Передача в функции массивОв
- •23.1. Передача одномерных массивов в функции
- •23.2. Передача двумерных массивов в функции
- •23.3. Передача в функции символьных строк
- •23.4. Возвращение указателей из функций
- •24. Функции с переменным количеством аргументов
- •24.1. Соглашения о вызовах: модификаторы функций
- •24.2. Объявление списка параметров переменной длины
- •25. Передача параметров в функцию main()
- •26. Указатели на функцию
- •27. Стандартные функцИи языка с
- •27.1. Функции для работы со строками
- •27.2. Функции для проверки символов и преобразования данных
- •27.3. Функция быстрой сортировки – gsort()
- •27.4. Функция двоичного поиска – bsearch()
- •28. Работа с файлами
- •28.1. Основные понятия
- •28.2. Основные функции для работы с файлами
- •28.3. Открытие и закрытие файлов
- •28.4. Ввод/вывод символов
- •28.5. Ввод/вывод строк
- •28.6. Форматированный ввод/вывод
- •28.7. Ввод/вывод блоков данных
- •28.8. Другие средства для работы с файлами
- •28.9. Ввод/вывод низкого уровня (префиксный доступ к файлам)
- •29. Типы, определяемые пользователем: Перечисления, структуры и объединения
- •29.1. Переименование типов – оператор typedef
- •29.2. Перечисления (enum)
- •29.3. Основные сведения о структурах
- •29.4. Структурные переменные в памяти компьютера
- •29.5. Доступ к полям структуры
- •29.6. Массивы структур
- •29.7. Структуры и функции
- •29.8. Объединения (union)
- •30. Динамическая память
- •30.1. Понятие динамического объекта
- •30.2 Создание и уничтожение динамических объектов
- •30.3 Динамическое размещение одномерных массивов и строк
- •30.4 Динамическое размещение двумерных массивов
- •30.5. Функции для работы с блоками памяти
- •31. Динамические структуры данных
- •31.1. Понятие структуры данных
- •31.2. Структуры, ссылающиеся на себя
- •31.3. Связанные списки
- •31.5. Очереди
- •Ниже приводятся примеры функций для очереди (структура элемента очереди совпадает со структурой элемента стека в примере выше):
- •32. Препроцессор языка с
- •32.1 Директива включения файлов
- •32.2. Директива определения макрокоманд (макросов)
- •32.3 Директива условной компиляции
- •32.4 Дополнительные директивы препроцессора
32.1 Директива включения файлов
Директива #include включает в текст программы содержимое указанного файла в ту точку исходного файла, где она записана. Включаемый файл также может содержать директивы #include. Эта директива может встречаться в любом месте программы, но обычно все включения размещаются в начале исходного текста. Директива имеет две формы:
#include "имя_файла"
#include <имя_файла>
Имя файла может состоять либо только из имени файла, либо из имени файла с предшествующим ему путем.
Если имя файла указано в кавычках, то поиск файла осуществляется в соответствии с заданным путем, а при его отсутствии – в текущем каталоге.
#include "my.h"
Если имя файла задано в угловых скобках, то поиск файла производится в стандартных каталогах (для их настройки служит опция Options→Directo-ries...→Include Directories).
#include <stdio.h>
Директива #include широко используется для включения в программу так называемых заголовочных файлов, содержащих прототипы библиотечных функций, и поэтому почти всегда программы на языке С начинаются с этой директивы.
32.2. Директива определения макрокоманд (макросов)
Директива #define служит для замены часто используемых констант, ключевых слов, операторов или выражений некоторыми идентификаторами. Идентификаторы, заменяющие текстовые или числовые константы, называют именованными константами. Идентификаторы, заменяющие фрагменты программ, называют макрокомандами (или макросами), причем макрокоманды могут иметь аргументы. Макрокоманды и именованные константы предназначены для улучшения восприятия программ и упрощения программирования. Директива имеет две формы:
#define идентификатор текст #define идентификатор(список_параметров) текст
Эта директива заменяет все последующие вхождения идентификатора на текст. Такой процесс называется макроподстановкой. После того, как макроподстановка выполнена, полученная строка вновь просматривается для поиска новых макросов. При этом ранее произведенные макроподстановки не принимаются во внимание.
Текст может представлять собой любой фрагмент программы на языке С, а также может и отсутствовать.
Именованная константа – это просто имя, которому присваивается постоянное значение (константа). Такая константа в отличие от значения переменной не может изменяться по мере выполнения программы. Кроме того, что именованные константы делают программу легче для восприятия, они еще и облегчают модификацию программ.
Следующий оператор определяет именованную константу MAX_N как значение 100:
#define MAX_N 100
Чтобы отличить именованную константу от переменной, обычно используют для именованных констант буквы верхнего регистра. После определения константы, можно использовать ее значение на протяжении всей программы, просто обращаясь к имени значения константы.
Обратить внимание! Определение константы не следует заканчивать точкой с запятой. Если вы поставите точку с запятой в конце определения, препроцессор включит ее в ваше определение. Например, если в директиве для MAX_N поставить точку с запятой после значения 100, то препроцессор в дальнейшем каждый экземпляр константы MAX_N заменит значением 100 с точкой с запятой: 100;.
Макросы могут использоваться для простой замены в тексте программы:
#define begin {
#define end }
В то же время, макросы с параметрами позволяют создавать что-то похожее на функции: похожее только внешне, но принципиально разное по реализации.
Параметризованный макрос для возведения в квадрат числа:
#define SQR(x) ((x)*(x))
Происходит замена в тексте всех идентификатор SQR(…) на ((…)*(…)) с подстановкой вместо x того, что записано в скобках. Эта замена носит чисто текстовый характер. Никаких вычислений или преобразований типа при этом не производится.
#define SQR(x) ((x)*(x))
void main() {
int i=10, x;
x = SQR(i); // подстановка: x = ((i)*(i));
x = SQR(2+i); // подстановка: x = ((2+i)*(2+i));
x = 1 / SQR(i); // подстановка: x = 1 / ((i)*(i));
}
Большое количество скобок в параметризованных макросах является только на первый взгляд лишним, ведь препроцессор понимает макрос буквально, т.е. каждый формальный параметр будет просто заменен на фактический:
#define SQR(x) (x*x)
void main() {
int i=10, x;
x = SQR(i); // подстановка: x = (i*i);
x = SQR(2+i); // подстановка: x = (2+i*2+i); ???
x = 1 / SQR(i); // подстановка: x = 1 / (i*i);
}
#define SQR(x) (x)*(x)
void main() {
int i=10, x;
x = SQR(i); // подстановка: x = (i)*(i);
x = SQR(2+i); // подстановка: x = (2+i)*(2+i);
x = 1 / SQR(i); // подстановка: x = 1 / (i)*(i); ???
}
Как видим, макрос с параметрами – это что-то похожее на функцию, но без вызова функции. Функции безусловно «правильней», но требуют от процессора дополнительных затрат на передачу параметров, вызов и возврат. Если функция часто вызывается, эти затраты (т.е. дополнительное время) могут быть велики и тогда полезно заменить функцию макросом.
В то же время, у макроса есть недостаток, который при определенных условиях может превратится в его достоинство, – отсутствие проверки типов аргументов. Функция, возводящая число в квадрат, принимает аргумент определенного типа. Для параметров типа int придется писать одну функцию, для double – другую. В то же время макрос, возводящий в квадрат, ничего не знает о типе параметра и его можно использовать для любых переменных.
Обратить внимание! Определение макроса не является функцией. Каждый раз при вызове функции программа помещает параметры в стек и затем выполняет переход к коду функции. После завершения функции программа удаляет параметры из стека и переходит обратно к оператору, который следует непосредственно за вызовом функции. В случае с макросом препроцессор просто выполняет замену в тексте программы каждой ссылки на макрос соответствующим определением макроса. Поэтому макросы выполняются быстрее функций, но они увеличивают размер выполняемой программы.
Задача. Разработать макросы для замены значений двух переменных и для нахож-дения максимума из двух переменных.
#define SWAP(a,b) \
a = a - b; \
b = b + a; \
a = b - a;
#define MAX((a),(b)) ((a)>(b)?(a):(b))
Задача. Разработать макросы для ускорения записи в программе цикла for.
#define FORin for(i=0; i<n; i++)
#define FOR(i,n) for(i=0; i<n; i++)
void main() {
int m[5] = {1,2,3,4,5}, s, n = 5, j, i, k ;
s = 0;
FORin // подстановка: for(i=0; i<n; i++)
s + =m[i];
s=0;
FOR(j,n) // подстановка: for(j=0; j<n; j++)
s += m[j];
s=0;
FOR(k,5) // подстановка: for(k=0; k<5; k++)
s += m[k];
}
Макросы в программах можно использовать различным образом. Только надо помнить, что цель использования макросов состоит в упрощении кодирования и улучшении восприятия программ.
Одиночный символ #, помещаемый перед параметром макроса, указывает на то, что параметр должен быть преобразован в символьную строку, то есть, конструкция вида #формальный_параметр будет заменена на конструкцию "фактический_параметр". Например, можно сделать макрос print для печати значения переменных в формате «имя = значение»:
#define print(a) printf(#a " = %d\n",a)
int abc = 10;
print(abc); // подстановка: printf("abc" " = %d\n", abc);
Наконец, возможна директива препроцессора для удаления из текста программы заданного идентификатора:
#define DEBUG
Препроцессор умеет не только создавать макросы, но и уничтожать их. Для уничтожения макросов используется директива #undef вида:
#undef идентификатор
Например:
#define MY_NULL 0
x = MY_NULL; // подстановка: x = 0;
#undef MY_NULL
y = MY_NULL; // нет подстановки: y = MY_NULL;