
- •Федеральное агентство по образованию
- •Краткая история языка с
- •Особенности и основные понятия языка Си
- •Структура программы
- •Типы данных
- •Классификация типов данных
- •Вещественные
- •Логический
- •Диапазоны представления данных
- •Объявление переменных
- •Константы в языке Си
- •Операции языка Си. Приоритет операций
- •Операция присваивания
- •Арифметические операции
- •Операции отношения
- •Логические операции
- •Поразрядные операции
- •Операцияsizeof
- •Операция выбора по условию
- •Операция запятая
- •Приведение типов Неявное преобразование типов
- •Явное преобразование типов
- •Основные средства ввода-вывода на Си Понятие потока
- •Стандартные потоки
- •Функции потокового ввода-вывода
- •Функции ввода
- •Ввод символов
- •Ввод строк
- •Форматированный ввод
- •Операторы языка Си
- •Оператор if
- •Оператор switch
- •Оператор while
- •Оператор do while
- •Оператор for
- •Оператор break
- •Оператор continue
- •Функции вывода на экран
- •Вывод символов
- •Функция вывода строк puts()
- •Форматированный вывод
- •Указатели в языке Си
- •Адресная арифметика
- •Массивы
- •Функции в языке с
- •Прототипы функций
- •Указатели типа void
- •Функции, возвращающие указатели
- •Встраиваемые функции
- •Рекурсивные функции
- •Функции с переменным числом аргументов
- •Аргументы функции main()
- •Область действия и область видимости
- •Локальные переменные
- •Локальные переменные в функции Main()
- •Глобальные переменные
- •Классы памяти
- •Указатели на указатели
- •Указатели и многомерные массивы
- •Массивы указателей
- •Указатели на функции
- •Операции над строками символов Определение длины строки
- •Копирование строк
- •Поиск в строках
- •Преобразования символов в строках
- •Другие функции для работы со строками
- •Преобразования строк в числа
- •Преобразования чисел в строки
- •Функции анализа символов
- •Распределение памяти
- •Манипулирование блоками памяти
- •Типы, определяемые пользователем
- •Перечислимый тип
- •Структуры
- •Сложные структуры
- •Битовые поля в структурах
- •Объединения
- •Работа с файлами Связь между потоками и файлами
- •Типы дисковых файлов
- •Открытие файла
- •Запись и чтение данных
- •Форматированный ввод-вывод Форматированный вывод
- •Int fprintf (file *fp, char *fmt, ...);
- •Форматированный ввод
- •Символьный ввод
- •Символьный вывод
- •Блочный ввод-вывод
- •Закрытие файла
- •Директивы препроцессора
- •Директива #include
- •Директива #define
- •Директивы условной компиляции
- •Директива #undef
- •Предопределенные макросы
- •69 Лекции по курсу «Языки программирования» Часть III
Классы памяти
При выборе класса памяти, т.е. способа хранения переменной в памяти при выполнении программы, может оказаться полезной следующая таблица, в которой сведены данные о пяти классах памяти в языке С.
Класс памяти |
Ключевое слово |
Время существования |
Область действия |
Автоматический |
auto |
временно |
блок |
Регистровый |
register |
временно |
блок |
Статический локальный |
static |
постоянно |
блок |
Статический глобальный |
static |
постоянно |
файл |
Внешний |
extern |
постоянно |
программа |
При выборе класса памяти, как правило, следует отдавать предпочтение автоматическим переменным, применяя остальные классы в случае необходимости. Вот еще некоторые рекомендации по назначению классов памяти.
Для начала сделайте все переменные автоматическими.
Если простая переменная должна использоваться очень интенсивно, например, как счетчик цикла, сделайте ее регистровой (добавьте к ее объявлению ключевое register).
В функциях, не являющихся main(), объявите статическими те переменные, которые должны сохранять свои значения между вызовами функций.
Если переменную используют все или почти все функции программы, сделайте ее внешней.
Указатели на указатели
Указатель — это числовая переменная, содержащая адрес другой переменной. Указатель объявляется с помощью знака звездочки (*). Например,
int *ptr;
Для того чтобы указатель указывал на определенную переменную, ему присваивается ее адрес с помощью операции взятия адреса (&). Пусть объявлена переменная Var
int. Var
ptr = &Var; // инициализация указателя.
var = 12 и *ptr=12 идентичны.
Поскольку указатель и сам является числовой переменной, он располагается в памяти по определенному адресу. Поэтому можно создать указатель на указатель — переменную, которая содержит адрес переменной-указателя. Вот как это делается:
int var= 12; /* Var имеет тип int. */
int *ptr = &Var; /* ptr - указатель на Var. */
int **ptr_ptr = &ptr; /* ptr_to_ptr - указатель на указатель на int.*/
Обратите внимание, что при объявлении указателя на указатель используется двойной знак операции ссылки по указателю (**). Соответственно, он же используется для обращения к значению переменной по указателю на указатель:
**ptr to_ptr =12;
Этот оператор присваивает значение 12 переменной Var.
А следующий оператор отображает значение Var на экране: printf("*%d, **ptr_to_ptr);
Если по ошибке воспользоваться одинарной, а не двойной операцией ссылки применительно к указателю на указатель, то ничего хорошего из этого не выйдет. Например, следующий оператор присваивает значение 12 указателю ptr:
*ptr_to_ptr =12;
В результате ptr начинает указывать на некий адрес 12 в памяти компьютера. Очевидно, это ошибка с потенциально неприятными последствиями.
Обращение к значению по указателю на указатель еще называют многократной, или вложенной ссылкой по указателю. Ограничений на глубину вложенности указателей не существует— можно объявить указатель на указатель на указатель... и так до бесконечности, но на практике нет смысла использовать больше двух уровней вложенности, так как возникающие при этом сложные конструкции слишком подвержены ошибкам.
Самое распространенное применение указателей на указатели связано с массивами указателей.