
- •Происхождение языка с
- •Язык среднего уровня
- •Структурированный язык
- •Язык программирования
- •Компиляторы против интерпретаторов
- •Вид программ на с
- •Библиотеки и компоновка
- •Раздельная компиляция
- •Карта памяти с-программы
- •Переменные, константы, операторы и выражения
- •Идентификаторы
- •Типы данных
- •Модификаторы типов
- •Модификаторы доступа
- •Объявление переменных
- •Локальные переменные
- •Формальные параметры
- •Глобальные переменные
- •Спецификаторы хранения
- •Статические переменные
- •Статические локальные переменные
- •Статические глобальные переменные
- •Регистровые переменные
- •Оператор присваивания
- •Многочисленное присваивание
- •Преобразование типов при присваивании
- •Инициализация переменных
- •Константы
- •Символьные константы с обратным слэшем
- •Операторы
- •Арифметические операторы
- •Увеличение и уменьшение
- •Операторы отношения и логические операторы
- •Битовые операторы
- •Оператор ?
- •Операторы указания & и *
- •Оператор 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
- •Операторы препроцессора # и ##
- •Предопределенные макросы
- •Комментарии
Модификаторы доступа
С имеет 2 типа модификаторов, которые используются для контроля за способом доступа или модификации переменных. Эти модификаторы называются const и volatile.
Переменные типа const не могут изменяться во время выполнения программы. Например: const int а; создаст целочисленную переменную, называемую а, которая не может быть модифицирована в программе. Она может использоваться в других типах выражений. Переменная с модификатором const получает свое значение или при инициализации, или каким-либо аппаратно-зависимым способом. Например, нижеприведенная строка присваивает count значение 100: const int count = 100; Помимо инициализации константа не может быть модифицирована программой.
Модификатор volatile используется для сообщения компилятору о возможности изменения значения способами, не определенными в программе. Например, адрес глобальной переменной может быть передан в подпрограмму часов операционной системы и использован для хранения времени системы. В данной ситуации содержимое переменной изменяется без использования оператора присваивания в программе. Это важно, поскольку компилятор автоматически оптимизирует некоторые выражения, делая предположения, что содержимое переменных не изменяется в выражениях. Также некоторые виды оптимизации могут изменять порядок вычисления выражений во время процесса компиляции. Модификатор volatile предотвращает возникновение данных изменений.
Возможно использование этих модификаторов вместе. Например, если предполагается, что 0x30 является адресом порта, содержимое которого изменяется внешним устройством, то следующее объявление предохранит от побочных эффектов: const volatile unsigned char *port = 0x30;
Объявление переменных
Все переменные должны объявляться перед использованием. Ниже показана стандартная форма объявления: тип список_переменных
Здесь тип должен быть корректным типом данных С, а список переменных может содержать одно или более имен идентификаторов, разделенных запятыми. Ниже показаны некоторые виды объявлений: int i, j, l; short int si; unsigned int ui; double balance, profit, loss; Помните, что в С имя переменной ничего не делает со своим типом.
Имеется три основных места, где объявляются переменные: внутри функций, при определении параметров функции и вне функций. Эти переменные называются соответственно локальными переменными, формальными параметрами и глобальными переменными.
Локальные переменные
Переменные, объявляемые внутри функций, называются локальными переменными. В некоторой литературе по С данные переменные могут называться автоматическими из-за использования ключевого слова auto. Поскольку термин локальные переменные широко используется, то далее мы будем пользоваться им. С локальными переменными могут работать только операторы, находящиеся в блоке, где данные переменные объявлены. Вне этого блока локальные переменные неизвестны. Следует помнить, что блок кода начинается открытием фигурной скобки и заканчивается закрытием фигурной скобки.
Наиболее важно понять то, что локальные переменные существуют только в блоке кода, в котором они объявлены. Таким образом, локальные переменные создаются при входе в блок и уничтожаются при выходе из него.
Наиболее типичным блоком кода, в котором объявляются локальные переменные, является функция. Например, рассмотрим две функции: void func1 (void) { int x; x= 10; ) void func2(void) { int x; x = -199; Целочисленная переменная x объявляется дважды: один раз в func1() и другой раз в func2(). х в func1() не имеет отношения к х в func2(), поскольку каждая х известна только в блоке, где произошло объявление переменной.
Язык С содержит ключевое слово auto, которое можно использовать для объявления локальных переменных. Тем не менее, поскольку предполагается, что все неглобальные переменные по умолчанию созданы с ключевым словом auto, то оно на самом деле никогда не используется.
Наиболее типично объявление всех необходимых для функции переменных в начале блока кода функции. Это выполняется, главным образом, для того, чтобы любой читающий код, знал об используемых переменных. Тем не менее, нет необходимости выполнять это, поскольку локальные переменные могут быть объявлены в любом блоке кода. (Они должны быть объявлены в начале блока, перед операторами, выполняющими какие-либо действия.) Для того, чтобы понять как все это работает, рассмотрим следующую функцию: void f(void) { int t; scanf ("%d", &t); if (t==1) { char s[80]; /* s существует только в данном блоке */ printf("введите имя:"); gets (s); process (s); } /* s здесь неизвестна */ } Здесь локальная переменная s известна только в блоке кода if. Поскольку s известна только в блоке кода if, то к ней не может быть осуществлен доступ откуда-либо еще — даже из других частей функции.
Возможность объявления переменной в собственном блоке кода, а не в начале функции, может предотвратить случайное ненужное употребление переменной где-либо в функции. По существу объявление переменных в блоке кода, использующего их, позволяет отделить код от данных.
Поскольку локальные переменные уничтожаются при выходе из функции, в которой они объявлялись, то эти переменные не могут хранить значение между вызовами функций. (Как будет видно, имеется возможность заставить компилятор сохранять значения путем использования модификатора static.)
Если не определено место для хранения локальных переменных, то они будут храниться в стеке. Тот факт, что стек является динамически изменяющейся областью памяти, объясняет, почему локальные переменные в общем не могут содержать значения между вызовами функций.