
- •Указатели.
- •4.16.1.Операция получения адреса &
- •Переменные указатели
- •Указатели должны иметь значение
- •Доступ к переменной по указателю
- •Указатель на void
- •Указатели-константы и указатели переменные
- •Передача простой переменной в функцию
- •Передача массивов
- •Указатели и адреса
- •Указатели и аргументы функций
Указатели.
Указатель - это переменная, содержащая адрес переменной. Указатели широко применяются в Си - отчасти потому, что в некоторых случаях без них просто не обойтись, а отчасти потому, что программы с ними обычно короче и эффективнее. Указатели и массивы тесно связаны друг с другом: в данной главе мы рассмотрим эту зависимость и покажем, как ею пользоваться. Наряду с goto указатели когда-то были объявлены лучшим средством для написания малопонятных программ. Так оно и есть, если ими пользоваться бездумно. Ведь очень легко получить указатель, указывающий на что-нибудь совсем нежелательное. При соблюдении же определенной дисциплины с помощью указателей можно достичь ясности и простоты. Мы попытаемся убедить вас в этом.
Изменения, внесенные стандартом ANSI, связаны в основном с формулированием точных правил, как работать с указателями. Стандарт узаконил накопленный положительный опыт программистов и удачные нововведения разработчиков компиляторов. Кроме того, взамен char* в качестве типа обобщенного указателя предлагается тип void* (указатель на void).
Для чего нужны указатели? Вот наиболее частые примеры их использования:
Доступ к элементам массива
Передача аргументов в функцию, от которой требуется изменить эти аргументы
Передача в функции массивов и строковых переменных
Выделение памяти
Создание сложных структур, таких, как связный список или бинарное дерево.
Идея указателей несложна. Каждый байт в памяти машины имеет свой уникальный адрес. Адреса начинаются с 0, а затем монотонно возрастают. Если у нас есть 1 Мб ОП, то максимальным адресом будет число 1 048 575.
Загружаясь в память, наша программа занимает некоторое количество этих адресов. Это означает, что каждая переменная и каждая функция нашей программы начинается с некоторого конкретного адреса.
4.16.1.Операция получения адреса &
Мы можем получить адрес переменной, используя операцию получения адреса &.
Int xl=ll, x2=22, x3=33;
&xl - возвращает адрес переменной xl;
&х2 - возвращает адрес переменной х2;
&хЗ - возвращает адрес переменной хЗ;
Реальные адреса, занятые в программе, зависят от многих факторов, таких, как компьютер, на котором запущена программа, размер ОП, наличие другой программы в памяти и т.д. По этой причине от запуска к запуску программа будет возвращать в общем случае разные адреса. Пусть мы получили следующие значения
Ox8f4ffff4 - адрес переменной xl;
Ox8f4ffff2 - адрес переменной х2;
Ox8f4ffffO - адрес переменной хЗ;
Важно понимать, что адрес переменной - это не то же самое, что значение переменной. Содержимое трех переменных - это числа 11, 22 и 33.
Заметим, что адреса отличаются друг от друга двумя байтами. Это произошло потому, что переменная типа int занимает в памяти два байта. Если бы мы использовали переменные типа char, то значения адресов отличались бы на единицу.
Переменные указатели
Адресное пространство ограничено. Нам необходимы переменные, хранящие значение адреса. Нам знакомы переменные, хранящие знаки, целые или вещественные числа и т.д. Адреса хранятся точно так же. Переменная, содержащая в себе значение адреса, называется переменной-указателем или просто указателем.
Какого же типа может быть переменная-указатель? Она не того же типа, что и переменная, адрес которой мы храним: указатель на int не имеет типа int.
Int *ptr; - определение переменной-указателя на целое. * - означает указатель на. Т.е. переменная ptr может содержать в себе адрес переменной типа int.
Компилятору необходимы сведения о том, какого именно типа переменная, на которую указывает указатель. Поэтому не существует обобщенный тип pointer такой, чтобы мы могли записать, например
pointer ptr;