Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SIT.doc
Скачиваний:
5
Добавлен:
01.07.2025
Размер:
349 Кб
Скачать
☆

Лекция 4 Указатели

Понимание и правильное использование указателей очень важно для создания хороших программ на языке С. Указатели необходимы для успешного использования функций и динамического распределения памяти. Кроме того. многие конструкторы С++ требуют применения указателей. Однако с указателями следует обращаться осторожно. Использование в программе неинициализированного указателя может привести к "зависанию" компьютера. При неаккуратном использовании указателей в программе могут возникнуть ошибки, которые очень трудно найти. Но и обойтись без указателей в программах на языке С нельзя.

Объявление указателей

Указатель - это переменная, которая содержит адрес некоторого объекта. Здесь имеется в виду адрес в памяти компьютера. Вообще-то это просто целое число, но нельзя трактовать указатель как переменную или константу целого типа. Если переменная будет указателем, то она должна быть соответствующим образом объявлена. Указатель объявляется следующим образом:

тип *<имя неременной>;

В этом объявлении тип - некоторый тип языка С, определяющий тип объекта, на который указывает указатель (адрес которого содержит).

* - означает, что следующая за ней переменная является указателем. Например:

char *ch;

int *temp, i, *j;

float *pf, f;

Здесь объявлены указатели ch, temp, j. pf, переменная i типа int и переменная f типа float.

Операции над указателями

С указателями связаны две специальные операции: & и *. Обе эти операции являются унарными, т.е. имеют один операнд, перед которыми они ставятся. Операция & cooтветствует операции "взять адрес". Операция * соответствует словам "значение, расположенное по указанному адресу".

Особенность языка С состоит в том, что знак * соответствует двум операциям, не имеющим друг к другу никакого отношения: арифметической операции умножения и операции взять значение.

В то же время спутать их в контексте программы нельзя, так как одна из операций унарная (содержит один операнд), другая - умножение - бинарная (содержит два операнда). Унарные операции & и * имеют наивысший приоритет наравне с унарным минусом.

В объявлении переменной, являющейся указателем, очень важен базовый тип. Откуда компилятор знает, сколько байт памяти занимает переменная, на которую указывает данный указатель? Ответ прост: из базового типа указателя. Если указатель имеет базовый тип int, то переменная занимает 2 байта, char - 1 байт и т.д.

Простейшие действия с указателями иллюстрируются следующей программой:

Программа 4.1:

#include<stdio.h>;

main() {

float x=10.1, y;

float *pf;

pf=&x;

y=*pf;

printf(“x=%f y=%f\n”, x, y); //печатает х==10.1 у= 10.1

++*рf;

printf(“x=%f y=%f\n, x, у); // печатает х=11.1 y= 10.1

у=(*рf)*у+4;

printf("х=%f у=%f\n”, х, у), // печатает х=11.1 у= 116.110008

return 0;}

К указателям можно применить операцию присваивания. Указатели одного и того же типа могут использоваться в операции присваивания, как и любые другие переменные. Рассмотрим пример.

Программа 4.2:

#include<stdio.h>

main()

{ int x=10;

int *p, *g;

p=&x;

g=p;

printf("%p", р); /* печать содержимого р */

printf("p", g); /* печать содержимого g */

printf("%d %d", х, *g); /* печать величины х и величины по адресу g*/

}

В языке С допустимо присвоить указателю любой адрес памяти. Однако, если объявлен указатель на целое int *pi; а по адресу, который присвоен данному указателю, находится переменная х типа float, то при компиляции программы будет выдано сообщение об ошибке в строке р=&х;

Эту ошибку можно исправить, преобразовав указатель на int к типу указателя на float явным преобразованием типа:

p=(int)&x;

В этом примере приведена еще одна спецификация формата функции printf() %p. Этот формат используется для печати адреса памяти в шестнадцатеричной форме. Нельзя создать переменную типа void, но можно создать указатель на тип void. Указателю на void можно присвоить указатель любого другого типа. Однако при обратном присваивании необходимо использовать явное преобразование указателя на void:

void *pv;

float f, *pf;

pf=&f;

pv=pf;

pf=(float*)pv;

При этом теряется информация о том, на какой тип указывал исходный указатель.

Программа 4.3:

#include<stdio.h>

/*Неправильныe действия с указателями*/

main()

{ float x=10.1, у;

int *p;

p=&x;

/* Потом заменим на p=(int*)&x: */

у=*p;

printf("х=%Г у=%f", x, y);

}

В результате работы этой программы не будет получен тот ответ, который ожидался. Переменной у не будет присвоено значение переменной х, так как будут обрабатываться не 4 байтa, как положено для переменной типа float, а только 2 байта, так как базовый тип указателя - int.

Как и над другими типами переменных, над указателями можно производить арифметические операции: сложение и вычитание. (Операции ++ и -- являются частными случаями операций сложения и вычитания.) Арифметические действия над указателями имеют свои особенности. Выполним простейшую программу.

Программа 4.4:

#include<stdio.h>

void main() {

int *p, *g;

int х=40;

p=&x;

рrintf(“*р=%d\n”, *p); // печатает *р=40

g=g+1;

printf(“p=%p\n”,p); // печатает р=3197:21F4

printf("g %p\n ", g); // печатает g=3197:21F6

рrintf(“р=%d\n", *p); // печатает *р=40

}

После выполнения этой программы мы увидим, что при операции ++ значение указателя р увеличилось не на 1, а на 2. И это правильно, так как новое значение указателя должно указывать не на следующий адрес памяти, а на адрес следующего целого. А целое, как мы помним, занимает 2 байта. Если бы базовый тип указателя был не int a double, то были бы напечатаны адреса, отличающиеся на 8, именно столько байт памяти занимает переменная типа double, т. е. при каждой операции ++р значение указателя будет увеличиваться на количество байт, занимаемых переменной базового типа указателя.

Операции над указателями не ограничиваются только операциями ++ и --. К указателям можно прибавлять некоторое целое или вычитать целое. Пусть указатель р имеет значение 2000 и указывает на целое. Тогда в результате выполнения оператора p=р+3; значение указателя р будет 2006. Если же указатель р1=2000 был бы указателем на float, то после применения оператора р1=р1+10 значение р1 было бы 2040.

Общая формула для вычисления значения указателя после выполнения операции р=р+n; будет иметь вид

<р>=<р>+n*<количество байт памяти базового типа указателя>.

Можно также вычитать один указатель из другого. Так, если р и р1 - указатели на элементы одного и тoгo же массива, то операция р - р1 дает такой же результат, как и вычитание индексов соответствующих элементов массива.

Другие арифметические операции над указателями запрещены, например, нельзя сложить два указателя, умножить указатель на число и т. д. Указатели можно сравнивать. Применимы все 6 операций сравнения: <, >, <=, >=, =, ==, !=.

Сравнение р<g означает, что адрес, находящийся в р, меньше адреса, находящеюся в g. Если р и g указывают на элементы одного массива, то индекс элемента, на который указывает p, меньше индекса массива, на который указывает g.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]