- •Основы алгоритмизации и программирования, язык Си
- •Введение
- •Блок-схема алгоритма Общие требования к блок-схеме алгоритма
- •Линейные и разветвляющиеся процессы
- •Циклические процессы
- •Итерационные процессы
- •Комментарии
- •Типы данных
- •Данные целого типа
- •Данные вещественного типа
- •Модификатор const
- •Переменные перечисляемого типа
- •Константы
- •Операции и выражения
- •Операция присваивания
- •Арифметические операции
- •Операции поразрядной арифметики
- •Логические операции
- •Операции отношения
- •Инкрементные и декрементные операции
- •Операция sizeof
- •Порядок выполнения операций
- •Приоритет операций
- •Преобразование типов
- •Операция приведения
- •Операция запятая
- •Ввод и вывод информации
- •Директивы препроцессора Директива #include
- •Директива #define
- •Понятие пустого и составного операторов
- •Условные операторы
- •Операторы организации цикла
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла do … while
- •Вложенные циклы
- •Операторы перехода (break, continue, return, goto)
- •Примеры программ
- •Массивы Одномерные массивы
- •Примеры программ
- •Многомерные массивы (матрицы)
- •Примеры программ
- •Указатели Понятие указателя
- •Описание указателей
- •Операции с указателями
- •Связь между указателями и массивами
- •Массивы указателей
- •Многоуровневые указатели
- •Примеры программ
- •Символьные строки
- •Ввод/вывод строк.
- •Функции работы со строками.
- •Примеры программ
- •Функции
- •Прототип функции.
- •Определение функции.
- •Параметры функции
- •Параметры по умолчанию
- •Передача массива в функцию
- •Inline функции
- •Класс памяти
- •Автоматические переменные
- •Статические переменные
- •Регистровые переменные
- •Блочная структура
- •Примеры программ
- •Указатели на функции
- •Примеры программ
- •Рекурсия
- •Примеры программ
- •Аргументы в командной строке
- •Функции с переменным числом параметров
- •Примеры программ
- •Сортировка
- •Пузырьковая сортировка.
- •Шейкер сортировка
- •Сортировка вставкой
- •Сортировка выбором
- •Метод Шелла
- •Метод Хора
- •Структуры
- •Доступ к элементам структуры
- •Инициализация структур
- •Указатели на структуры.
- •Структуры и функции
- •Примеры программ
- •Поля бит
- •Объединения
- •Переменные с изменяемой структурой
- •Примеры программ
- •Организация списков и их обработка
- •Операции со списками при связном хранении
- •Построение обратной польской записи
- •Односвязный линейный список, очередь
- •Двусвязный линейный список
- •Циклический список, кольцо
- •Двусвязный циклический список
- •Примеры программ
- •Деревья
- •Потоки и файлы
- •Файлы Основные сведения о файловой системе
- •Организация посимвольного ввода и вывода
- •Определение конца файла feof()
- •Организация ввода и вывода строк
- •Удаление файлов
- •Дозапись потока
- •Позиционирование в файле
- •Текстовые и двоичные файлы
- •Функции fread() и fwrite()
- •Примеры программ
- •Хеширование
- •Схемы хеширования
- •Метод открытой адресации с линейным опробыванием
- •Метод цепочек
- •Машинное представление графов
- •Примеры программ
- •Литература
Указатели Понятие указателя
В языках высокого уровня обычно нет необходимости знать адреса памяти, где расположены данные и оперирующие ими переменные. В С(С++) предусмотрен удобный механизм получения адресов данных. И соответственно, если имеется адрес, то несложно получить (или разместить) значение по этому адресу.
Указатель - это переменная (группа ячеек), содержащая адрес некоторой переменной или группы переменных(массив). Если переменная объявлена как указатель, то она содержит адрес памяти, по которому может находится скалярная величина любого типа. При объявлении переменной типа указатель, необходимо определить тип объекта данных, адрес которых будет содержать переменная, и имя указателя с предшествующей звездочкой (или группой звездочек). Формат объявления указателя:
[ модификатор ] спецификатор_типа * идентификатор.
Память ЭВМ можно представить в виде массива последовательно пронумерованных и проадрессованных ячеек, содержимым которых можно оперировать по отдельности или связными группами. Унарная операция & выдает адрес объекта, таким образом, инструкция p=&k; присваивает адрес первого байта переменной k в переменную p. В этом случае говорят, что p указывает на k. Различие между ними состоит в том, что p - это переменная, а &k - константа. Операция & применяется только к объектам, непосредственно расположенным в памяти (переменные, элементы массива, структуры). Ее операндом не может быть ни выражение, ни константа, ни регистровая переменная.
Если мы имеем переменную p, являющуюся ссылкой (адресом), то для доступа к значению по этому адресу можно воспользоваться операцией косвенной адресации (*). Выражение kk=*p - позволяет переменной kk передать значение, на которое указывает p (расположенное по адресу p).
Как уже отмечалось, для того чтобы иметь возможность оперировать с некоторым объектом, он должен быть описан как объект определенного типа. Аналогично и с указателями на эти объекты.
Описание указателей
Из рассмотренного ранее материала следует, как описать переменную любого из известных типов. Как описать переменную типа "указатель"? Для этого необходимо указать, на переменную какого типа возможна ссылка через объявляемый указатель. Это связано с тем, что для выполнения некоторых адресных операций необходимо знать размеры памяти, отводимой под объекты, на которые указывает указатель. Ниже приводятся примеры описания указателей:
int *p; // p - указатель на элементы типа int
float *k,*k1; // k и k1 – два указателя на элементы типа float
char *s; // s – указатель на элементы символьного типа
Адрес - это целое число без знака, поэтому при выводе на печать значения указателя (p,k,k1,s) будем использовать формат %u. Исключением является безтиповый указатель, то есть указатель вида:
void *pp;
Указатель типа void* указывает на место в оперативной памяти, но не содержит информации о типе объекта и может быть использована для адресации данных любого типа. Однако для выполнения арифметические и логические операции над этими указателями или объектами, на которые они указывают, необходимо при выполнении каждой операции явно определить тип объекта. Это может быть выполнено с помощью операции приведения типов. Иначе компилятору неизвестно, какой длины поле памяти должно использоваться в операции.
Для правильной работы указатель должен ссылаться на объекты только соответствующего ему типа. При этом компилятором не контролируется нарушение этого требования. Рассмотрим эту ситуацию на примере перезаписи значения переменной a в переменную b.
#include <stdio.h>
void main(void)
{ int *p;
float a=10.25,b;
p= &a;
b= *p;
printf(“a= %f b= %f”,a,b);
}
Значение переменной a не будет перенесено в переменную b, так как p является целочисленным указателем. И так как р адресует двухбайтную ячейку памяти, то в переменную b будут переписаны только 2, а не 4 байта из переменной a.