- •Основы программирования на языке Си.
- •Достоинства языка Си.
- •Будущее языка Си.
- •Использование языка Си.
- •Использование текстового редактора для подготовки программ.
- •Исходные и выполняемые файлы
- •Пример простой программы на языке Си
- •Структура простой программы
- •2. Лекция: Данные, символьные строки, директива #define.
- •Основные типы данных
- •Описание различных типов, переменные и константы
- •Символьные строки
- •Препроцессор языка Си
- •3. Лекция: Операции.
- •Основные операции
- •Дополнительные операции
- •Перечень операций языка Си
- •Операции, уровень приоритета которых равен 1
- •Операции, уровень приоритета которых равен 2
- •Операции, уровень приоритета которых равен 3
- •Операции, уровень приоритета которых равен 4
- •Операции, уровень приоритета которых равен 5
- •Операции, уровень приоритета которых равно 6
- •Операции, уровень приоритета которых равен 7
- •Операция, уровень приоритета которой равен 8
- •Операция, уровень приоритета которой равен 9
- •Операция, уровень приоритета которой равен 10
- •Операция, уровень приоритета которой равен 11
- •Операция, уровень приоритета которой равен 12
- •Операция, уровень приоритета которой равен 13
- •Операция, уровень приоритета которой равен 14
- •Операция, уровень приоритета которой равен 15
- •Примеры простых задач
- •4. Лекция: Операторы.
- •Выражения
- •Простейшие выражения
- •Операторы
- •Составные операторы
- •Оператор цикла while
- •10 Км. В последующие дни он проезжал со скоростью,
- •Изучение и использование функций printf( ) и scanf( )
- •Применение функции scanf( )
- •Подведем итог.
- •5. Лекция: преобразование типов.
- •Эквивалентность типов
- •Преобразование типов
- •Разбор программы
- •Операция приведения
- •Неявное преобразование типа
- •Арифметические преобразования
- •Явные преобразования типов
- •Синтаксис типов
- •6. Лекция: функции и переключение ввода-вывода
- •Ввод и вывод одного символа
- •Чтение одной строки
- •Чтение файла
- •Переключение и работа с файлами
- •7. Лекция: Выбор вариантов.
- •Выбор вариантов
- •Оператор if
- •Расширение оператора if
- •Операции отношения
- •Логические операции
- •Операция условия: ?:
- •Множественный выбор
- •8. Лекция: Циклы и другие управляющие средства. Структурное программирование.
- •Структурное программирование
- •Цикл с предусловием
- •Цикл со счетчиком
- •Цикл с постусловием
- •Другие управляющие операторы
- •9. Лекция: Функции.
- •Создание и использование функций
- •Аргументы функции
- •Возвращение значений
- •Локальные переменные
- •Нахождение адресов
- •Указатели, первое знакомство
- •Функции с переменным количеством аргументов
- •10. Лекция: Классы памяти и разработка программ.
- •Классы памяти и область действия
- •Автоматические переменные
- •Внешние переменные
- •Статические переменные
- •Внешние статические переменные
- •Регистровые переменные
- •11. Лекция: Препроцессор языка Си.
- •Общие сведения
- •Символические константы: #define
- •Использование аргументов с #define
- •Макроопределение или функция?
- •Включение файла: #include
- •Условная компиляция
- •12. Лекция: Массивы и указатели.
- •Указатели и массивы
- •Массивы
- •Указатели
- •Динамические объекты
- •Создание динамических объектов
- •Доступ к динамическим объектам
- •Связь между указателями и массивами
- •Инициализация массивов и классы памяти
- •Функции, массивы и указатели
- •Операции с указателями
- •13. Лекция: Символьные строки и функции над ними.
- •Строковые константы
- •Массивы символьных строк и их инициализация
- •Массив и указатель: различия
- •Указатели и строки
- •Ввод-вывод строк
- •Обработка строк
- •14. Лекция: Структуры.
- •Определение структурных переменных
- •Доступ к компонентам структуры
- •Поля битов в структурах
- •Объединения
- •Перечисления
- •Переменные структуры
- •Указатели и структуры
- •Массив структур
- •Переименование типов
- •15. Лекция: Библиотека языка Си и файлы ввода-вывода.
- •Стандартные библиотечные функции
- •Связь с файлами
- •Текстовые файлы с буферизацией
- •Распределение памяти
- •16. Лекция: Функции в примерах.
- •Функция получения случайных чисел
- •Поиск узлов из простых чисел
- •Матрица инцидентности
- •Структуры данных
- •Все операции со стеком
Явные преобразования типов
Выражения могут быть преобразованы из одного типа в другой явным указанием. Выражение Eможет быть явно преобразовано к типуимя-типас помощью записи вида
(имя - типа) Е
где имя типа представляется в форме
указатель-типа абстрактный-описатель
Абстрактный описатель аналогичен описателю, за исключением того, что он не содержит определяемого или описываемого идентификатора. Смысл слов имя-типа, представляемого в форме
Т абстрактный описатель
где Т является указателем типа, может быть определен одним из таких способов:
форма абстрактного описателя - смысл слов "Т абстрактный описатель";
пустой (абстрактный описатель) - абстрактный описатель типа Т;
*(абстрактный описатель) - указатель на тип Т;
абстрактный описатель ( ) - функция, возвращающая значение типа Т;
абстрактный описатель [n] - массив с n элементами типа Т, n - выражение с постоянным значением;
Приведем примеры явного преобразования. Предположим, что даны следующие определения и описания:
int i;
char *pc, *name;
char *calloc( ), *strcpy( );
тогда можно привести следующие примеры явных преобразований типов:
(char) i- преобразует значение типаintв значение типаchar.
pc=(char *) 0777- преобразует восьмеричный литер0777в значение указателя на знак таким образом, что оно может быть присвоено переменной "pc".
(emp *) calloc(1,sizeof(emp))- преобразует значение "знакового" указателя, возвращаемого функциейcalloc, в значение указателяemp.
(void) strcpy(name,"gehani")- опускает значение, возвращенное функциейstrcpy.
Синтаксис типов
Можно отметить, что синтаксис типов в языке Си нерегулярен и беспорядочен, о чем свидетельствуют:
- Трудность создания форматеров.
- Большое число ошибок, допускаемых даже опытными программистами.
- Трудность создания синтаксических анализаторов для трансляторов с языка Си - не существует двух трансляторов с языка Си, синтаксис входного языка для которых полностью совпадал бы.
Отсутствие формального описания синтаксиса языка Си - даже книга (Kernighan, B.W., and D.M. Ritchie, The C Programming Language, prentice-Hall, Engleewood Cliffs, NJ (1978)) может служить лишь черновым описанием такового. Сказанное касается в полной мере и синтаксиса типов. Имеется три случая, когда необходимо использовать типы:
Описания, связывающие тип с именем, как, например, в случае описания (глобальных) переменных и формальных параметров;
Описания, связывающие значения (и тип) с именем, как, например, описания программ;
Приведения, обеспечивающие, например, возможность трактовки символов как целых.
6. Лекция: функции и переключение ввода-вывода
-Ввод и вывод одного символа.
-Буферы.
-Чтение одной строки.
-Чтение файла.
-Переключение и работа с файлами.
Ввод и вывод одного символа
В данном разделе мы рассмотрим функции, применяемые при вводе и выводе. Кроме того, мы коснемся других аспектов этого понятия. Под функциями ввода-вывода подразумеваются функции, которые выполняют транспортировку данных в программу и из нее. Мы уже использовали две такие функции: printf( )иscanf( ). Теперь рассмотрим несколько других возможностей, предоставляемых языком Си.
Функции ввода-вывода не входят в определение языка Си. Их разработка возложена на программистов, реализующих компилятор с языка Си. С другой стороны, выгода использования стандартного набора функций ввода-вывода на всех системах очевидна. Это дает возможность писать переносимые программы, которые легко можно применять на разных машинах. В языке Си имеется много функций ввода-вывода такого типа, например printf( )иscanf( ). Ниже мы рассмотрим функцииgetchar( )иputchar( ).
Эти две функции осуществляют ввод и вывод одного символа при каждом обращении к ним. Этот способ ввода данных лучше соответствует возможностям машины. Более того, такой подход служит основой построения большинства программ обработки текстов, являющихся последовательностями обычных слов. Мы увидим, как можно применять эти функции в программах, занимающихся подсчетом символов, чтением и копированием файлов. Узнаем про буферы, эхо-печать и переключение ввода-вывода.
Функция getchar( )получает один символ, поступающий с пульта терминала (и поэтому имеющий название), и передает его выполняющейся в данный момент программе. Функцияputchar( )получает один символ, поступающий из программы, и пересылает его для вывода на экран. Рассмотрим пример программы, которая принимает один символ с клавиатуры, и выводит его на экран:
/*ввод-вывод*/
#include <stdio.h>
main( )
{
char ch;
ch=getchar( ); /***1***/
putchar(ch); /***2***/
}
Для большинства систем спецификации функции getchar иputchar содержатся в системном файлеstdio.h, поэтому мы указали данный файл в программе. Функцияgetchar( )аргументов не имеет, т.е. при ее вызове в круглые скобки не помещается никакая величина. Она просто получает очередной поступающий символ, и сама возвращает его значение выполняемой программе. Оператор, приведенный в строке 1, присваивает значение функцииgetchar( )переменнойch. Функцияputchar( )имеет один аргумент. При ее вызове необходимо в скобках указать символ, который требуется вывести на печать. Аргументом может быть одиночный символ (включая знаки, представляемые управляющими последовательностями), переменная или функция, значением которой является одиночный символ. Правильным обращением к функцииputchar( )является указание любого из этих аргументов при ее вызове:
putchar('D');
putchar('\n');
putchar('\007');
putchar(ch); /* переменная типа char */
putchar(getchar( ));
Модифицируем нашу программу:
#include <stdio.h>
main( )
{
putchar(getchar( ));
}
Такая запись очень компактна и не требует введения вспомогательных переменных. В результате компиляции такая программа оказывается более эффективной.
Буферы
При выполнении данной программы (любой из двух ее версий), вводимый символ в одних вычислительных системах немедленно появится на экране (эхо-печать), в других же вычислительных системах ничего не происходит до тех пор, пока мы не нажмем клавишу Enter. Первый случай относится к небуферизованному (прямому) вводу, означающему, что выводимый символ оказывается немедленно доступным ожидающей программе. Второй случай служит примером буферизованного ввода, когда вводимые символы собираются и помещаются в некоторой области временной памяти, называемой буфером. Нажатие клавиши Enter приводит к тому, что блок символов, или один символ, становится доступным программе. В нашей программе применяется только первый символ, поскольку функция getchar( ) вызывается в ней один раз.
Зачем нужны буферы?
Во-первых, оказывается, что передачу нескольких символов в виде одного блока можно осуществить гораздо быстрее, чем передавать их последовательно по одному. Во-вторых, если при вводе символов допущена ошибка, мы сможем воспользоваться корректирующими средствами терминала, чтобы ее исправить. И когда мы нажмем клавишу Enter, будет произведена передача откорректированной строки. Однако для некоторых диалоговых программ небуферизованный ввод может оказаться приемлемым. Например, в программах обработки текстов было бы желательно, чтобы каждая команда вводилась, как только мы нажимаем соответствующую клавишу. Поэтому как буферизованный, так и небуферизованный ввод имеет свои достоинства.
Рассмотрим вывод на печать групп символов. Желательно, чтобы в любой момент можно было остановить работу программы. Для этого напишем программу так, чтобы она прекращала работу при получении какого-нибудь специального символа, например "!":
/* ввод-вывод */
/* ввод и печать символов до поступления
завершающего символа */
#include <stdio.h>
#define STOP '!' /*дает символу '!' символическое имя */
main( )
{
char ch;
ch=getchar( ); /***9***/
while(ch != STOP) { /***10***/
putchar( ch); /***11***/
ch=getchar( ); /***12***/
}
}
В данном примере при первом прохождении тела цикла функция putchar( )получает значение своего аргумента в результате выполнения оператора, расположенного в строке 9. В дальнейшем, вплоть до завершения работы цикла, значением этого аргумента является символ, передаваемый программе функциейgetchar, расположенной в строке 12. Циклwhileбудет осуществлять чтение и печать символов до тех пор, пока не поступит признак STOP.
! Программа, приведенная ниже, делает то же самое, но стиль ее написания лучше отвечает духу языка Си:
/* ввод-вывод */
#include <stdio.h>
#define STOP '!'
main( )
{
char ch;
while ((ch=getchar( )) != STOP) /***8***/
putchar(ch);
}
Одна строка 8 этой программы заменяет строки 9, 10, 12 предыдущей программы.