int x = 1, y = 2;
int *ptr; // объявили указатель на целую переменную
ptr = &x; // взяли адрес переменной х = 2
y = *ptr; // переменная y стала равной 1
*ptr = 0; // переменная х стала равной 0
Пример объявления указателя:
int *ptr;
Следует помнить, что любой указатель может указывать только на объекты одного конкретного типа данных, заданного при объявлении. Унарный оператор * есть оператор косвенного доступа. Примененный к указателю, он выдает объект, на который данный указатель ссылается. Одноместные (унарные) операции * и & имеют более высокий приоритет для своих операндов, чем арифметические операции.
Для указателей одного типа можно, например, выполнять присваивание без разыменования, поскольку указатели сами по себе являются переменными. Пусть определен еще один указатель типа int, например ptr2. Тогда возможно произвести присвоение:
ptr2 = ptr;
После этого указатель ptr2 будет указывать на ту же переменную, что и указатель ptr.
В языке С допустимы следующие основные операции над указателями: присваивание; получение значения того объекта, на который он указывает (синонимы: косвенная адресация, разыменование, раскрытие ссылки); получение адреса самого указателя; унарные операции изменения значения указателя; аддитивные операции и операции сравнений (отношений).
Унарные операции ++ и -- позволяют позиционировать указатель на следующую и предыдущую ячейки памяти, в которых хранятся значения типов, связанных с типом указателя. При этом значение указателя меняется на величину, определяемую размером соответствующего типа. Например, для указателя типа char* операция ++ увеличит значение адреса на sizeof (char), для указателя типа int* операция -- уменьшит значение адреса на sizeof(int) и т. д. Это свойство унарных операций ++ и –– используется для последовательного обращения к значениям одного типа, связанного с типом указателя, хранящимся в смежных ячейках памяти. В таком смысле унарные операции ++ и –– сходны с операциями увеличения и уменьшения счетчика цикла при последовательном обращении к элементам массива.
Следует особо остановиться на указателях и квалификаторе (модификаторе) const. Как известно, квалификатор const превращает переменную в константу, значение которой не должно меняться. Например, нет смысла изменять число p. Значение константы должно быть инициализировано в месте ее определения. В связи с этим различают указатели на константы и константные указатели. Приведем пример:
long value = 9999L;
const long *pvalue = &value;
Последняя строчка данного кода представляет собой указатель на константу. Попытка указателю pvalue присвоить иное числовое значение будет восприниматься компилятором как ошибка [6]. Но само значение переменной value изменять допустимо. При этом указатель содержит адрес переменной, значение которой изменилось. В то же время саму переменную можно объявить с помощью квалификатора const. В этом случае нельзя изменять ни переменную, ни значение указателя (т. е. присвоить ему иное числовое значение). Указатели на константы часто используются как формальные параметры функций (о функциях будет сказано позднее).
Константный указатель может адресовать как константу, так и переменную. В случае когда определен константный указатель, через него уже нельзя брать адрес другой переменной. Рассмотрим пример определения константного указателя:
int count = 43;
int *const pcount = &count;
Вторая строчка приведенного кода определяет и инициализирует константный указатель pcount, который «привязан» к адресу переменной count. Если определить новую переменную того же типа, то взять адрес новой переменной с помощью константного указателя pcount будет нельзя, компилятор сделает сообщение об ошибке и работа программы станет невозможной. В то же время возможно изменить значение константного указателя через другое числовое значение. Но это повлечет за собой изменение переменной, адрес которой содержится в указателе. Например,
int count = 43;
int *const pcount = &count;
pcount = 345;
В приведенном коде переменная count будет иметь значение 345.
Соответственно для константного указателя на константный объект (например, на константную переменную) ни значение объекта, ни значение самого указателя (когда будет сделана попытка присвоить иное числовое значение указателю) не могут быть изменены в программе. Например,
const int card = 21;
const int *const pcard = &card;
Перечисленные особенности указателей с квалификатором const присущи и переменным (объектам) других типов.
Указатели, значения которых изменять нельзя, используются, например, при заполнении константных таблиц.
Пример 5. Написать программу определения адресов целых чисел от 0 до 9 и строчных букв латинского алфавита.
Программный код решения примера:
#include <stdio. h> #include <conio. h> int main (void) { int i, j = 0; char c = 'a', *ptr2; // Адрес символа 'a' ptr2 = &c; printf("\n\t Figures, symbols and their addresses:\n"); for (i = 0; i < 10; ++i) { printf("\n\t %3d) %2d --> %5p", i + 1, i, &i); } printf("\n"); for ( ; *ptr2 <= 'z'; (*ptr2)++) { printf("\n\t %3d) %2c --> %5p", ++j, *ptr2, ptr2); } printf("\n\n Press any key: "); _getch(); return 0; } |
В программе тело цикла заключено в фигурные скобки, что необязательно, но в профессиональном программировании часто так поступают. Результат выполнения программы показан на рис. 5.1. |

Рис. 5.1. Вывод адресов цифр и строчных букв
В программе использован спецификатор формата %5p для определения адреса переменных. Число 5 определяет отступ от левого края на пять позиций.
6. Указатели и массивы в языке С
Одной из наиболее распространенных конструкций с использованием указателей являются массивы. В результате применения указателей для массивов требуется меньшее количество используемой памяти и обеспечивается высокая производительность.
В языке С массивы – это упорядоченные данные (элементы) одного типа. Компилятор языка С рассматривает имя массива как адрес его первого элемента. Например, если имя массива Arr с десятью элементами, то компилятор преобразует i-й элемент (0 £ i < 10) по правилам работы с указателями с операцией разыменования: *(Arr + i). Здесь Arr как бы указатель, а i – целочисленная переменная. Сумма (Arr + i) указывает на i-й элемент массива, а операция разыменования (оператор раскрытия ссылки *) дает его значение.
Имя массива без индекса образует указатель на начало этого массива.
Следует помнить следующее: отличие имени массива от указателя состоит в том, что имя массива не может быть изменено. Оно всегда указывает на одно и то же место в памяти – на нулевой элемент.
Пусть, например, массив Arr содержит 10 целочисленных переменных:
int Arr[10];
Тогда можно объявить указатель ptr, который будет указывать на элементы массива Arr:
int *ptr;
Тип указателя (в примере это int) должен соответствовать типу объявленного массива.
Для того чтобы указатель ptr ссылался на первый элемент (с нулевым индексом) массива Arr, можно использовать утверждение
ptr = Arr;
В то же время можно применить прямую адресацию:
ptr = &Arr[0];
Обе формы записи эквивалентны.
Аналогичные утверждения будут справедливы для других типов массивов: char, float, double и пр.
Если указатель ptr указывал на первый элемент (с нулевым индексом) массива Arr, то для обращения к следующему элементу массива допустимы такие формы утверждений:
ptr = &Arr[1];
ptr += 1;
Соответственно выражение *(ptr+1) будет ссылаться на значение, содержащееся в элементе Arr[1].
Утверждение
ptr += n;
заставит указатель *ptr ссылаться на элемент массива, находящийся на расстоянии n от того, на который ранее ссылался указатель, независимо от типа элементов, содержащихся в массиве. Разумеется, значение n должно быть в допустимых пределах для данного объема массива.
При работе с указателями и массивами особенно удобны операторы инкремента – ++ и декремента – ––. Использование оператора инкремента с указателем аналогично операции суммирования с единицей, а операция декремента имеет тот же эффект, что и вычитание единицы из указателя.
В языке программирования С вполне корректной операцией является сравнение указателей. К указателям применяются операции сравнения >, >=, !=, ==, <=, < . Сравнивать указатели допустимо только с другими указателями того же типа или с константой NULL, обозначающей значение условного нулевого адреса. Константа NULL – это особое значение переменной-указателя, присваиваемое ей в том случае, когда она не должна иметь никакого значения. Его можно присвоить переменной-указателю любого типа. Оно представляет собой целое число нуль. Особое значение имеет сравнение двух указателей, которые связаны с одним и тем же массивом данных.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Основные порталы (построено редакторами)
