
- •Происхождение языка с
- •Язык среднего уровня
- •Структурированный язык
- •Язык программирования
- •Компиляторы против интерпретаторов
- •Вид программ на с
- •Библиотеки и компоновка
- •Раздельная компиляция
- •Карта памяти с-программы
- •Переменные, константы, операторы и выражения
- •Идентификаторы
- •Типы данных
- •Модификаторы типов
- •Модификаторы доступа
- •Объявление переменных
- •Локальные переменные
- •Формальные параметры
- •Глобальные переменные
- •Спецификаторы хранения
- •Статические переменные
- •Статические локальные переменные
- •Статические глобальные переменные
- •Регистровые переменные
- •Оператор присваивания
- •Многочисленное присваивание
- •Преобразование типов при присваивании
- •Инициализация переменных
- •Константы
- •Символьные константы с обратным слэшем
- •Операторы
- •Арифметические операторы
- •Увеличение и уменьшение
- •Операторы отношения и логические операторы
- •Битовые операторы
- •Оператор ?
- •Операторы указания & и *
- •Оператор sizeof
- •Оператор «запятая»
- •Операторы [ ] u ()
- •Приоритеты в с
- •Выражения
- •Преобразование типов в выражениях
- •Принудительные преобразования
- •Пробелы и круглые скобки
- •Сокращенные операторы в с
- •Операторы управления программой
- •Истина и ложь в с
- •Операторы выбора
- •Вложенные if
- •Лесенка if-else-if
- •Оператор ?
- •Вложенные операторы switch
- •Вариации цикла for
- •Бесконечный цикл
- •Циклы for без тела
- •Метки и goto
- •Функции
- •Оператор return
- •Выход из функции
- •Возвращаемые значения
- •Значения, возвращаемые функцией main()
- •Правила видимости для функций
- •Аргументы функции
- •Передача по значению и передача по ссылке
- •Создание передачи по ссылке
- •Передача массивов в функции
- •Аргументы функции main()
- •Функции, возвращающие нецелые значения
- •Использование прототипов функции
- •Прототипы стандартных библиотечных функций
- •Создание прототипов функций, не имеющих параметров
- •Возврат указателей
- •Рекурсия
- •Сопоставление классического и современного объявления параметров
- •Указатели на функции
- •Особенности реализации
- •Параметризированные функции и функции общего назначения
- •Эффективность
- •Массивы
- •Одномерный массив
- •Создание указателя на массив
- •Передача одномерных массивов в функции
- •Двумерные массивы
- •Массивы строк
- •Многомерные массивы
- •Индексация с помощью указателей
- •Размещение массивов
- •Инициализация массива
- •Инициализация безразмерных массивов
- •Пример программы игры в крестики-нолики
- •Указатели
- •Указатели - это адреса
- •Переменные-указатели
- •Операторы для работы с указателями
- •Выражения с указателями
- •Присваивание указателей
- •Арифметические действия с указателями
- •Сравнение указателей
- •Динамическое выделение и указатели
- •Указатели на константы
- •Указатели на константы
- •Указатели на константы
- •Указатели и массивы
- •Указатели на символьные массивы
- •Массивы указателей
- •Указатели на указатели - многочисленное перенаправление
- •Инициализация указателей
- •Указатели на функции
- •Проблемы, связанные с указателями
- •Структуры, объединения и определяемые пользователем типы
- •Структуры
- •Доступ к членам структуры
- •Присваивание структур
- •Массивы структур
- •Программа инвентаризации
- •Передача структур в функции
- •Передача членов структур в функции
- •Передача всей структуры в функцию
- •Указатели на структуры
- •Объявление указателя на структуру
- •Использование указателей на структуру
- •Массивы и структуры в структурах
- •Битовые поля
- •Объединения
- •Перечисления
- •Использование sizeof для обеспечения переносимости
- •Ввод, вывод, потоки и файлы
- •Потоки и файлы
- •Текстовые потоки
- •Двоичные потоки
- •Консольный ввод/вывод
- •Чтение и запись символов
- •Чтение и запись строк: gets() и puts()
- •Форматированный консольный ввод/вывод
- •Печать символов
- •Вывод чисел
- •Вывод адресов
- •Спецификатор %n
- •Модификаторы формата
- •Спецификатор минимума ширины поля
- •Спецификатор точности
- •Выровненный вывод
- •Работа с другими типами данных
- •Модификаторы * u #
- •Спецификаторы формата
- •Ввод чисел
- •Ввод беззнаковых целых
- •Чтение отдельных символов с помощью scanf()
- •Чтение строк
- •Ввод адреса
- •Спецификатор %n
- •Использование множества сканирования
- •Пропуск нежелательных специальных символов
- •Обычные символы в управляющей строке
- •В scanf() следует передавать адреса
- •Модификаторы формата
- •Подавление ввода
- •Файловая система ansi с
- •Указатель на файл
- •Открытие файла
- •Запись символа
- •Чтение символа
- •Использование fopen(), getc(), putc() и fclose()
- •Использование feof()
- •Две расширенные функции: getw() и putw()
- •Работа со строками: fgets() и fputs()
- •Fseek() и произвольный доступ
- •Удаление файлов
- •Работа с консолью
- •Препроцессор и комментарии
- •Директивы условной компиляции
- •Использование defined
- •Операторы препроцессора # и ##
- •Предопределенные макросы
- •Комментарии
Бесконечный цикл
Одним из наиболее интересных аспектов использования цикла for является создание бесконечного цикла. Поскольку не требуется ни одна из частей, можно создать бесконечный цикл путем удаления части проверки условия. Например: for ( ; ; ) printf(" this loop will run forever. \n");
Хотя можно использовать части инициализации и увеличения, наиболее типично использование for( ; ; ) без выражений для создания бесконечного цикла.
На самом деле конструкция for( ; ; ) не обязательно создает бесконечный цикл, поскольку в теле цикла может присутствовать оператор break, при достижении которого цикл оканчивает работу. Нижеприведенная программа контролирует нажатие клавиш и, в случае достижения необходимого условия, бесконечный цикл прерывается: for ( ; ; ) { ch = getchar(); /* ввод символа */ if (ch == 'A') break; /* выход из цикла */ } printf("you typed an A"); Цикл будет работать до тех пор, пока на клавиатуре не будет набрана А.
Циклы for без тела
Оператор может быть пустым. Это означает, что тело цикла for (или любого другого цикла) может быть пустым. Это можно использовать для улучшения эффективности некоторых алгоритмов, а также для создания задержек.
Одной из наиболее типичных задач программирования является удаление пробелов из потом ввода. Например, база данных может допускать запрос типа «покажите весь баланс меньше,чем 400 ». Базе данных необходимо иметь каждое слово запроса отдельно, без пробелов. То есть процессор ввода в базу данных распознает команду «показать», но не распознает « показать».Следующий цикл удаляет все пробелы из потока, на который указывает str: for ( ; *stг == ' ' ; str ++) ; Как видно, в цикле отсутствует тело.
В программах также часто используются циклы задержек. Следующий пример показывает, как создать задержку с помощью for: for (t = 0; t < SOME_VALUE; t++) ;
WHILE
Следующий итерационный оператор, доступный в С, - это while. Его стандартный вид следующий while (условие) оператор; где оператор - это или пустой, или простой, или составной оператор. Условием может любое выражение, имеющее в качестве истины ненулевое значение. Цикл выполняется, пока условие истинно. Когда условие становится ложным, выполняется строка, следующая за циклом. Следующий пример показывает процедуру, обрабатывающую ввод с клавиатуры, работающую пока не будет введен символ А: void wait_for_char(void) { char ch; ch = '\0'; /* инициализация ch */ while(ch!='A') ch = getchar (); } Сначала ch устанавливается в 0, Цикл while начинается с проверки ch на равенство А. Поскольку ch инициализировано нулем, проверка выдаст истину и цикл начнется. При каждом нажатии клавиши на клавиатуре условие проверяется повторно. Если введено А, условие становится ложным и цикл прекращается.
Как и цикл for, цикл while сначала выполняет проверку, то есть тело цикла может вообще не выполняться. Благодаря этому можно не выполнять отдельные проверки перед циклом. Хорошей иллюстрацией этого является функция pad(), добавляющая пробелы к концу строки до достижения предопределенной длины. Если строка уже имеет необходимую длину, то пробелы не будут добавляться: /* Добавляет пробелы к концу строки */ void pad (char *s, int length) { int l; l = strlen(s); /* определение длины */ while (l<length) { s[l] = ' '; /* вставка пробела */ l++; } s [ l ] = '\0'; /* строки должны заканчиваться нулем */ } Аргументами pad() являются s - указатель на удлиняемую строку и length - желаемая длина s. Если строка s имеет длину, равную или больше length, то код внутри цикла не будет выполняться. Если длина строки s меньше чем length, то pad() добавляет требуемое число пробелов к строке. Функция strlen(), являющаяся частью стандартной библиотеки, возвращает длину строки.
Когда необходимо несколько различных условий для окончания цикла while, как правило, используют одну переменную, являющуюся результатом действия этих условий, причем значение этой переменной может устанавливаться в различных частях цикла. Например: void func1 (void) { int working; working = 1; /* т.е. истина */ while(working) { working=process1(); if(working) working=process2(); if(working) working=process3(); } } Здесь любая из трех подпрограмм может вернуть ложь и вызвать остановку цикла. В теле цикла может и не быть никаких операторов. Например: while ((ch = getchar()) != 'А'); просто зацикливается до тех пор, пока с клавиатуры не введется буква А. Если нет уверенности в использовании оператора присваивания внутри проверки выражения цикла while, надо помнить, что знак равенства на самом деле просто оператор, вычисляющий значение стоящего справа операнда.
DO/WHILE
В противоположность циклам for и while, сначала проверяющим условие, цикл do/while проверяет условие в конце. То есть, цикл do/while всегда выполняется, по крайней мере, один раз. Стандартный вид цикла do/while следующий: do { последовательность операторов; } while (условие);
Хотя в фигурных скобках нет необходимости при наличии одного оператора, они обычно используются для улучшения читабельности и устранения недоразумений (у читателя, а не у компилятора) по поводу цикла while. Данный цикл do/while осуществляет чтение чисел с клавиатуры до тех пор, пока одно из них не станет меньше либо равно 100: do { scanf ("%d", &num); } while (num > 100);
Возможно, наиболее типичным использованием цикла do/while является процедура выбора пунктов меню. Когда набран корректный ответ, она возвращает значение функции. Неправильный ответ приводит к повторному вводу. Следующий пример являет собой улучшенную версию меню программы проверки орфографии: void menu(void) { char ch; printf("1. Check Spellirig\n"); printf("2. Correct Spelling Errors\n"); printf("3. Display Spelling Errors\n"); printf(" Enter your choice: "); do { ch = getche(); /* чтение клавиатуры */ switch(ch) { case '1' : check_spelling (); break; case '2' : correct_errors (); break; case '3' : display_errors(); break; } } while(ch!='1' && ch!='2' && ch!='3'); } В случае функции меню необходимо выполнить ее, по крайней мере, один раз. После отображения опций программа зацикливается до тех пор, пока не будет осуществлен корректный выбор.
BREAK
Оператор break имеет два назначения. Первое - это окончание работы оператора switch. Второе - это принудительное окончание цикла, минуя стандартную проверку условия. Данное назначение здесь и рассматривается. Когда оператор break встречается в теле цикла, цикл немедленно заканчивается и выполнение программы переходит на строку, следующую за циклом. Например: #include <stdio.h> int main(void) { int t; for(t=0; t<100; t++) { printf ("%d ", t) ; if (t==10) break; } return 0; } Данная программа выводит числа от 0 до 10 включительно и заканчивает работу, поскольку break вызывает немедленный выход из цикла, минуя условие t< 100.
Оператор break, как правило, используется в циклах, где особые условия могут вызвать немедленное завершение работы. Ниже приведен пример того, как нажатие на клавишу может остановить выполнение функции look_up(): int look_up(char *name) { char tname[40]; int loc; loc = -1; do { loc = read_next_name(tname); if(kbhit ()) break; } while(!strcmp(tname, name)); return loc; } Можно использовать функции такого типа для поиска имени в файле базы данных. Если файл слишком длинный и надоело ждать, то можно нажать на клавишу и выйти из функции до окончания работы. Функция kbhit() возвращает 0, если не было нажатия. В противоположном случае она возвращает ненулевое значение, break вызывает выход из самого внутреннего цикла. Например: for(t=0; t<100; ++t) { count = 1; for(;;) { printf ("%d ", count); count++; if (count == 10) break; } } выводит числа от 1 до 10 включительно 100 раз. Каждый раз, когда встречается break, контроль передается внешнему циклу for.
break, используемый в операторе switch, влияет только на данный switch, но не на цикл, в котором может находиться switch.
EXIT()
Функция exit(), находящаяся в стандартной библиотеке, вызывает немедленное окончание работы программы. Поскольку функция exit() останавливает выполнение программы и форсирует возврат в операционную систему, она используется для управления устройствами, и ее использует подавляющее большинство программ. Функция exit() имеет следующий вид: void exit (int статус); Она использует заголовочный файл stdlib.h. Значение статуса возвращается в операционную систему.
Для индикации корректности завершения работы exit() традиционно вызывается с аргументом 0.
Другие аргументы используются для индикации различного рода ошибок. Можно также использовать предопределенные макросы EXIT_SUCCESS и EXIT_FAILURE в качестве значений для статуса.
exit() используется, когда условия выполнения программы неудовлетворительны. Например компьютерная игра может требовать специальный графический адаптер. Функция main() данной игры может выглядеть следующим образом: #include <stdlib.h> int main(void) { if (!special_adaptor()) exit(1); play (); return 0; } где special_adaptor() - это определенная пользователем функция, возвращающая истину, если необходимый адаптер присутствует. Если карта в системе отсутствует, то данная функция возвращает ложь и программа завершает работу.
В другом примере exit() используется для выхода из программы и возврата в операционную систему void menu(void) { char ch; printf ("1. Check Spelling\n"); printf ("2. Correct Spelling, Errors\n"); printf("3. Display Spelling Errors\n"); printf("4. Quit\n"); printf(" Enter your choice: "); do { ch = getchar(); /* чтение клавиатуры */ switch(ch) { case '1': check_spelling(); break; case '2': correct_errors(); break; case '3': display_errors (); break; case '4': exit(0); /* возврат в ОС*/ } } while(ch!='1' && ch!='2' && ch!='3'); }
CONTINUE
Работа оператора continue чем-то похожа на работу оператора break. Но вместо форсированного окончания continue переходит к следующей итерации цикла, пропуская оставшийся код цикла. Например, следующая процедура выводит только положительные числа: do { scanf("%d", &x); if(x<0) continue; printf("%d ", x); } while(x!=100);
В циклах while и do/while оператор continue вызывает переход к проверке условия и затем продолжает работу цикла. В случае for выполняется часть увеличения, затем проверяется условие и, наконец, выполняется само тело цикла. Предыдущий пример может быть изменен для вывода только 100 чисел следующим образом: for(t=0; t<100; ++t) { scanf ("%d", &x); if (x<0) continue; printf ("%d ", x); }
В следующем примере оператор continue используется для ускорения выхода из цикла путем форсирования проверки, выполненной ранее: void code(void) { char done, ch; done = 0; while (!done) { ch = getchar(); if (ch == '.') { done = 1; continue; } putchar(ch+1); /* сдвиг алфавита на одну позицию */ } } Можно использовать данную функцию для кодирования сообщений, сдвигая все символы на 1 вверх, то есть 'a' станет 'b'. Функция завершает работу при обнаружении точки, и вывода на экран символа, соответствующего точке, не происходит, поскольку благодаря continue выполнение, минуя оператор вывода, переходит к условию проверки, которое обнаруживает, что done истинно, и вызывает выход.