Указатель
— это переменная, содержащая
адрес некоторого элемента данных (переменной, константы, функции, структуры). В
языке C указатели тесно связаны с обработкой массивов и
строк, которые будут рассмотрены в следующем разделе.
Для
объявления переменной как указателя используется оператор *:
Для
присвоения адреса некоторой переменной указателю используется оператор &,
например:
char *p; // объявляем указатель (способен содержать адрес
хранения любого символа);
char c = 'А'; //
объявляем символьную переменную с;
р = &c; // теперь p содержит адрес хранения символа
c.
Для
того чтобы извлечь значение переменной, на которую указывает указатель, используется
оператор разыменования *.
char *p;
char b; char c = 'А';
р = &c;.
b = *р; //
теперь b равняется не
адресу хранения переменной c, а значению
переменной c, то есть 'А'.
Аналогичным
образом, этот оператор можно использовать и для присвоения некоторого значения
переменной, на которую указывает указатель:
char *p;
char b, char c = 'А';
р = &b; //
теперь p содержит адрес хранения ещё не инициализированной
переменной b;
*р = b; //
инициализируем переменную b значением 'А'.
Применительно
к программированию микроконтроллеров, указатели можно использовать, к примеру,
для записи данных в порт ввода/вывода. Предположим, регистр данных порта
расположен в памяти по адресу 0x16. В этом случае, для записи в него значения 0xFF можно воспользоваться следующим фрагментом
программного кода:
unsigned char *p; //объявление
указателя на символ
p = 0x16; //
присваиваем указателю адрес порта PORTx
*p = 0xFF; // теперь на выводах порта PORTx будут все единицы.
Передача в функции
параметров по ссылке.
С
помощью указателей, используемых в качестве параметров функций, можно
организовать возврат более одного значения. Рассмотрим следующий пример:
int SumAndDiv(int *a, int *b) /* Объявляем функцию, возвращающую сумму от
целочисленного деления без остатка и остатка */
{
int bufA, bufB; //
Объявление целых чисел – bufA и bufB;
bufA = *a; // bufA = значению, на которое
указывает указатель а;
bufB = *b; // bufB = значению, на которое
указывает указатель b;
*a = bufA / bufB; //Указатель
a ссылается на
результат целочисленного
деления без остатка
*b = bufA % bufB; //Указатель
b ссылается на
остаток от целочисленного
деления
return bufA + bufB; //Функция возвращает сумму
…
int vl, v2, sum;
vl = 10;
v2 = 3;
sum = SumAndDiv(&vl, &v2); //sum = 4;
vl = 3;
v2 = 1.
В функции SumAndDiv вначале сохраняются в буферных переменных значения, на которые
указывают указатели а и b. Затем в
переменную, на которую указывает указатель а
записывается результат деления переданных в функцию значений без остатка, а в
переменную, на которую указывается указатель b — остаток от такого деления.
Поскольку с помощью указателей мы напрямую обращались к ячейкам памяти, а не к
переменным, то после выхода из функции содержимое этих ячеек остается
неизменным. При вызове функции SumAndDiv в нее
передаются адреса переменных vl и v2, которым в теле функции соответствуют указатели
а и b.
Указатели на структуры
На структуры можно создавать указатели
точно так же, как и для любого другого типа данных. Пример применения такого
указателя:
Struct DATE
{
int Day;
int Month;
int Year;
}
struct
DATE MyBirthday, *dateP;
dateP = SMyBirthday; //dateP указывает на
структуру
MyBirthday
В таком случае для доступа к полям
структуры через указатель используется onepaтор ->:
dateP->Day = 7;
dateP->Month = 8;
dateP->Year = 1974;
Можно также использовать и
разыменование указателя:
(*dateP).Day = 7;
(*dateP).Month = 8;
(*dateP).Year = 1974;
ПРИМЕЧАНИЕ
В последнем примере операция *dateP заключена в скобки,
поскольку оператор разыменования * имеет меньший приоритет по сравнению с оператором доступа к полю структуры.
Указатели также могут быть полями структуры.
Это часто используется для создания в памяти цепочек однотипных структур, когда
каждая предыдущая указывает на следующую:
struct
DATE
{
int Day;
int Month;
int Year;
struct DATE *next_date; //указатель на другую структуру того же типа
}
struct
DATE datel, date2;
datel.next_date
= &date2;
Теперь, к примеру, оператору datel.next_date->Year
соответствует доступ к полю Year структуры date2 (то есть, это эквивалентно записи date2.Year).