
- •Федеральное агентство по образованию
- •Краткая история языка с
- •Особенности и основные понятия языка Си
- •Структура программы
- •Типы данных
- •Классификация типов данных
- •Вещественные
- •Логический
- •Диапазоны представления данных
- •Объявление переменных
- •Константы в языке Си
- •Операции языка Си. Приоритет операций
- •Операция присваивания
- •Арифметические операции
- •Операции отношения
- •Логические операции
- •Поразрядные операции
- •Операцияsizeof
- •Операция выбора по условию
- •Операция запятая
- •Приведение типов Неявное преобразование типов
- •Явное преобразование типов
- •Основные средства ввода-вывода на Си Понятие потока
- •Стандартные потоки
- •Функции потокового ввода-вывода
- •Функции ввода
- •Ввод символов
- •Ввод строк
- •Форматированный ввод
- •Операторы языка Си
- •Оператор if
- •Оператор switch
- •Оператор while
- •Оператор do while
- •Оператор for
- •Оператор break
- •Оператор continue
- •Функции вывода на экран
- •Вывод символов
- •Функция вывода строк puts()
- •Форматированный вывод
- •Указатели в языке Си
- •Адресная арифметика
- •Массивы
- •Функции в языке с
- •Прототипы функций
- •Указатели типа void
- •Функции, возвращающие указатели
- •Встраиваемые функции
- •Рекурсивные функции
- •Функции с переменным числом аргументов
- •Аргументы функции main()
- •Область действия и область видимости
- •Локальные переменные
- •Локальные переменные в функции Main()
- •Глобальные переменные
- •Классы памяти
- •Указатели на указатели
- •Указатели и многомерные массивы
- •Массивы указателей
- •Указатели на функции
- •Операции над строками символов Определение длины строки
- •Копирование строк
- •Поиск в строках
- •Преобразования символов в строках
- •Другие функции для работы со строками
- •Преобразования строк в числа
- •Преобразования чисел в строки
- •Функции анализа символов
- •Распределение памяти
- •Манипулирование блоками памяти
- •Типы, определяемые пользователем
- •Перечислимый тип
- •Структуры
- •Сложные структуры
- •Битовые поля в структурах
- •Объединения
- •Работа с файлами Связь между потоками и файлами
- •Типы дисковых файлов
- •Открытие файла
- •Запись и чтение данных
- •Форматированный ввод-вывод Форматированный вывод
- •Int fprintf (file *fp, char *fmt, ...);
- •Форматированный ввод
- •Символьный ввод
- •Символьный вывод
- •Блочный ввод-вывод
- •Закрытие файла
- •Директивы препроцессора
- •Директива #include
- •Директива #define
- •Директивы условной компиляции
- •Директива #undef
- •Предопределенные макросы
- •69 Лекции по курсу «Языки программирования» Часть III
Указатели в языке Си
Указатель — это переменная, содержащая адрес другой переменной.
Объявление указателя выглядит следующим образом:
имя_типа *имя_указ;
имя_типа — любой из типов данных С. Он определяет тип переменной, на которую указывает указатель.
Звездочка (*) — это знак операции ссылки по указателю, и в данном случае он означает, что имя_указ является указателем на переменную типа имя_типа.
Указатели можно объявлять вместе с обычными переменными.
Инициализация указателей
Инициализируется указатель оператором присваивания:
указатель=&переменная,
& - операция взятия адреса.
Когда знак операции (*) стоит перед именем указателя, такая конструкция обозначает переменную, на которую указывает указатель, а операция называется разыменованием. .
Использование значения перемен путем указания ее имени называется прямым обращением к переменной. Использование значения переменной через указатель на нее называется косвенным обращении или ссылкой по указателю (адресу).
Потенциальные опасности
Указатели могут нести потенциальные опасности. Это связано с использованием неинициализированного указателя в левой части оператора присваивания.
Например, в этом операторе объявляется указатель на значение типа int
int *ptr;
Этот указатель еще не инициализирован, поэтому ни на что не указывает. Точнее он не указывает ни на что известное. У неинициализированного указателя есть какое-то значение, просто мы его не знаем. Во многих случаях это 0. Допустим, этот неинициализированный указатель используется в операторе присваивания: *ptr = 12;
Значение 12 помещается по адресу, на который указывает ptr. Этот адрес может относиться к произвольному месту памяти, где, например, размещается операционная система или код самой программы. Значение 12 может затереть важную информацию, и в результате это может привести к чему угодно: от странных ошибок выполнения программы до полного краха системы.
Поэтому необходимо корректно инициализировать указатели перед тем, как их использовать. Это необходимо делать самостоятельно, ни в коем случае не полагаться на компилятор.
Если переменная многобайтная, то адрес переменной фактически представляет собой адрес первого (младшего) байта из занимаемых ею.
Хотя указатели могут быть полезны и при работе с простыми переменными, наиболее удобны они в работе с массивами. В языке С указатели и массивы тесно связаны.
Имя массива без квадратных скобок является указателем на первый элемент массива. Например, если объявлен массив data[], то data — это адрес первого элемента данного массива. Получить адрес первого элемента массива можно и таким способом: &data[0];
В языке С справедливо соотношение (data == &data[0]).
Имя массива является постоянным указателем (адресной константой): его нельзя изменить, и оно остается фиксированным все время выполнения программы. Поэтому при работе можно объявить отдельный указатель и инициализировать его, чтобы он указывал на массив.
Пример.
short arr1[6], *p_arr1;
float arr2[3], *p_arr2;
p_arr1= arr1; p_arr2= arr2;
В массиве short – arr1 следующий элемент массива находится на два байта дальше в памяти, чем предыдущий и адрес следующего элемента соответственно на 2 больше, чем адрес предыдущего. В массиве float – arr2 адрес каждого следующего элемента на 4 больше, чем предыдущего.
Как же обращаться к последовательным элементам массива с помощью указателя? Из приведенных примеров видно, что указатель нужно увеличивать на 2 для перебора элементов типа short, и на 4 — в массиве типа float. Обобщая, можно сказать, что указатель увеличивается на sizeof(тип_данных) для обращения к следующему элементу.