русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Назначение и смысл указателей


Дата добавления: 2014-03-21; просмотров: 1529; Нарушение авторских прав


Указатели

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

Указатели – это переменные, содержащие адреса других переменных или функций, в том числе членов классов (каких-либо программных объектов, например, переменных, массивов, записей, функций). Указатель на функцию содержит адрес точки входа в функцию. Размер памяти, требуемый для хранения адреса и формат этого адреса, зависят от вычислительной платформы и реализации компилятора. В современных системах общего назначения размер указателя на данные любого типа составляет 4 байта:

sizeof(void*…long double*) ~ 4

 

Указатели являются необычными переменными по своему назначению – хранение адресов других переменных, но обычными по некоторым своим свойствам, например, выполняемым операциям. Поскольку адреса являются целыми беззнаковыми числами (длинными), то указатели также относятся к простым структурам данных (целочисленным).

Так как указатель – это адрес некоторого объекта, то через него можно обращаться к этому объекту. Унарная операция & позволяет получить адрес программного объекта, поэтому оператор

y = &x;

присваивает адрес объекта x переменной y. Операцию & можно применять только к объектам, действительно размещенным в памяти, конструкции вида

&(x+7)

&28

недопустимы.

Унарная операция * воспринимает свой операнд как адрес некоторого объекта и использует этот адрес для выборки содержимого, поэтому оператор

z = *y;

присваивает переменной z значение, записанное по адресу y.

Рассмотрим ситуацию размещения в памяти переменной x:



 

  A000  
значение x A001 Адрес x (&x) -> y = &x
  A002  
     
  A000  
*y y  
  A002  

z = *x

z = *(&x)

z = x

Итак, если y = &x и z = *y

то z = x

x – переменная

y – её адрес (указатель)

z – содержимое адреса y

*y – содержимое адреса y

 

Объекты, состоящие из знака * и адреса, необходимо объявлять.

int *a,*b,*c;

Указатели объявляются при помощи символа *. Объявление вида

char *d;

говорит о том, что значение, записанное по адресу d, имеет тип char. Описатель, записываемый после символа *, может представлять собой более сложную конструкцию. При этом тип объекта, на который «указывает» указатель, определяется совокупностью оставшейся части описателя и спецификатора типа:

char *(*(*var)())[10];

Указатель может указывать на значение, имеющее стандартный тип, в том числе перечислимый, а также на структуры, объединения, массивы, функции, объекты классов, члены классов и другие указатели.

При определении указателя часто бывает целесообразно выполнить его инициализацию. В качестве инициализирующего выражения должно использоваться константное выражение, в частности:

- явно заданный адрес участка памяти;

- указатель, уже имеющий значение;

- выражение, позволяющее получить адрес объекта с помощью операции &:

int i,*ip = &i;

После определения с инициализацией указателя ip доступ к переменной i возможен как с помощью её имени i, так и с помощью её адреса, являющегося значением указателя ip.

Присвоив указателю адрес объекта, можно не только получать, но и изменять содержимое этого объекта.

Выражение вида *ip обладает некоторыми правами имени объекта, т.е. *ip служит синонимом или псевдонимом имени i. Такое выражение (т.е. *ip) может использоваться везде, где допустимо использование имён объектов того типа, к которому относится указатель, но только в том случае, если указатель инициализирован при определении явным образом. Например, указатель а не инициализирован, поэтому попытки использовать выражение *а в левой части операции присваивания, или в функции ввода неправомерны (именно поэтому мы говорим лишь о некоторых правах имени объекта). Значение указателя (т.е. адрес как место в памяти) неизвестно, а результат занесения значения в неопределенный участок памяти непредсказуем и может привести к аварийному событию.

*а = 1; // Ошибочное применение

 

L-выражения или леводопустимые выражения – выражения, ссылающиеся на некоторую именованную область или ячейку памяти и поэтому имеющие смысл в левой части операции присваивания (откуда и произошло это название). Простейшим примером L-выражения является имя переменной – оно ссылается на ячейку памяти, которая хранит значение этой переменной.

Адрес ячейки, на которую ссылается L-выражение, может быть получен с помощью операции получения адреса &, за некоторыми исключениями: не могут быть получены адреса битовых полей и переменных, имеющих класс памяти register.

К модифицируемым L-выражениям (т.е. ссылающимся на ячейку памяти, значение которой доступно изменениям) относятся:

- идентификаторы переменных целых, плавающих, перечислимых типов, указателей, объектов классов, структур и объединений;

- индексные выражения, кроме тех, которые имеют тип массива;

- выражения выбора элемента класса, структуры или объединения, если выбранный элемент сам является одним из допустимых L-выражений;

- выражения косвенной адресации, т.е. получение значения по указателю, если только их значения не имеют типы «массив» или «функция»;

- L-выражения в скобках;

- ссылки на объекты;

- выражения преобразования типа переменной, если размер результирующего типа не превышает размера первоначального типа (см. пример).

char *p;

int i;

long n;

(long*)p = &n; // допустимо

(long)i = n; // ошибка

 

Немодифицируемые L-выражения (т.е. праводопустимые) – такие, адрес которых может быть получен, но использоваться в левой части бинарной операции присваивания они не могут: идентификаторы массивов, функций, констант, переменных, объявленных с модификатором const, операции вызова функций (для этих операций существуют исключения, которые будут рассмотрены ниже).

Специальное применение имеет указатель на тип void («пустой»), который означает, что данный указатель адресует любой объект, не имеющий тип const или volatile. Указатель на тип void в языках Си/Си++ является аналогом нетипизированного указателя pointer в Паскале. Любой указатель (кроме указателей на члены классов) может быть преобразован к указателю на void. Такое преобразование может быть неявным. Никакие другие преобразования типов указателей по умолчанию не выполняются, т.е. для обратного преобразования, – указателя на void в указатель на реальный объект, – требуется операция приведения типа. Другими словами, все указатели как бы «знают», что они «произошли» от указателя на void, но указатель на void «не знает», что от него «произошли» какие-то другие указатели.

char c, *p = &c;

void* v = p; // Неявное преобразование

char* q = (char*)v; // Явное приведение типа

 

Такое явное приведение типа указателя на void к типу, отличному от void*, требуется при выполнении операций с указателями на void, либо с адресуемым им объектом. Например, если объявлена переменная i типа int и указатель p на тип void, то можно присвоить p указателю адрес переменной i, но нельзя изменить значение указателя на void.

int i;

void* p;

p = &i;

p++; // Ошибка

(int*)p++;

 

Можно создать функцию с типом возвращаемого значения – указатель на void, возвращенное ею значение может быть присвоено указателю на тот тип, который требуется.

 

Константы при использовании указателей распространяются как на сам указатель, так и на объект, адресуемый этим указателем. Либо то, либо другое, либо оба вместе в этом случае являются константой. В объявлении указателя позиция модификатора const определяет, что указатель или им адресуемый объект не должен изменяться. Рассмотрим примеры:

char a,b;

 

const char *pcc = &a; // Указатель на const char – неизменяемый объект

pcc = &b; // Правильно

*pcc = ‘z’; // Неправильно

 

char *const cpc = &a; // Константный указатель на char – неизменяемый указатель

cpc = &b; // Неправильно

*cpc = ‘z’; // Правильно

Адрес константного объекта не может быть присвоен неконстантному указателю, т.к. такое присваивание может изменить константу через указатель на неё.

 



<== предыдущая лекция | следующая лекция ==>
Представление и структуры хранения логической информации | Операции с адресами


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.338 сек.