Студопедия — Доступ к значению переменной, хранящейся по адресу, с использованием операции разыменования называется непрямым доступом или разыменованием указателя.
Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

Доступ к значению переменной, хранящейся по адресу, с использованием операции разыменования называется непрямым доступом или разыменованием указателя.

Указатель на void

Отметим одну особенность указателей. Адрес, который мы помещаем в указатель, должен быть одного с ним типа. Мы не можем присвоить указателю на int адрес переменной типа float.

float fx=98.6;

int ptr = &fx; //так нельзя, типы int * и float * несовместны

Однако есть одно исключение. Существует тип указателя, который может указывать на любой тип данных. Он называется указателем на тип void и определяется следующим образом:

void *ptr.

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

int ix;

float fx;

int *iptr;

float *fptr;

void *vptr;

iptr =&ix;

fptr = &fx;

vptr = &ix;

vptr = &fx;

Следующие строки недопустимы

iptr - &fx;

fptr = &ix;

 

Указатели-константы и указатели переменные

Можно ли записать *(аггау++), т.е использовать операцию увеличения вместо прибавления шага j к имени array? Нет, так писать нельзя, поскольку нельзя изменять константы. Выражение array является адресом в памяти, где размещен наш массив, поэтому array - это указатель

константы. Мы не можем сказать аггау++, так же как не можем сказать 7++. Мы не можем увеличивать адрес, но имеем возможность увеличить указатель, который содержит этот адрес.
int main()

{

int array[5]={3 1,54,77,52,93};

intj;

int *prt;

prt = array;

for(j=0; j<5; j++) printf("%6d",*(prt++);

return 0;

}

Здесь мы определили указатель на int и затем присвоили ему значение адреса массива. После этого мы можем получить доступ к элементам массива, используя операцию разыменования *(prt++). Переменная pit имеет тот же адрес, что и array, поэтому доступ к первому элементу осуществляется как и раньше. Но prt - это уже переменная-указатель, то мы ее можем увеличивать. После этого она уже будет указывать на первый элемент массива array.

Передача простой переменной в функцию

Рассмотрим функцию void summ(int a, int b, int *us). 3-ий параметр объявлен в этой функции как указатель на int. Когда головная программа вызывает функцию summ, то она передает в качестве 3 аргумента адрес переменной summ(a, b,&s). Это не сама переменная, а ее адрес. Т.к. функция summ получает адрес переменной s, то она может применить к ней операцию разыменования *us = а+b. Т.к. us содержит адрес переменной s, то все действия, которые мы совершим с *us, мы в действительности совершим с s.

Передача массивов

Пусть мы хотим передать в функцию массив int A[10]. Прототип такой функции может выглядеть следующим образом void func(int[]). В этом случае мы обеспечиваем передачу массива по значению. Если мы хотим использовать аппарат указателей, то передача аргумента будет выглядеть как void func(int *).

Т.к. имя массива является его адресом, то нет нужды использовать при вызове функции операцию взятия адреса, т.е можно записать func(A).

Указатели и адреса

Начнем с того, что рассмотрим упрощенную схему организации памяти. Память типичной машины подставляет собой массив последовательно пронумерованных или проадресованных ячеек, с которыми можно работать по отдельности или связными кусками. Применительно к любой машине верны следующие утверждения: один байт может хранить значение типа char, двухбайтовые ячейки могут рассматриваться как целое типа short, а четырехбайтовые - как целые типа long. Указатель - это группа ячеек (как правило, две или четыре), в которых может храниться адрес. Так, если c имеет тип char, а p - указатель на c, то ситуация выглядит следующим образом:

Унарный оператор & выдает адрес объекта, так что инструкция

p = &c;

присваивает переменной p адрес ячейки c (говорят, что p указывает на c). Оператор & применяется только к объектам, расположенным в памяти: к переменным и элементам массивов. Его операндом не может быть ни выражение, ни константа, ни регистровая переменная.

Унарный оператор * есть оператор косвенного доступа. Примененный к указателю он выдает объект, на который данный указатель указывает. Предположим, что x и y имеют тип int, а ip – укаэатель на int. Следующие несколько строк придуманы специально для того, чтобы показать, каким образом объявляются указатели и как используются операторы & и *.

int х = 1, у = 2, z[10];

int *ip; ip - указатель на int

 

ip = &x; теперь ip указывает на x

y = *ip; y теперь равен 1

*ip = 0; x теперь равен 0

ip = &z[0]; ip теперь указывает на z[0]

 

Объявления x, y и z нам уже знакомы. Объявление указателя ip

int *ip;

мы стремились сделать мнемоничным - оно гласит: "выражение *ip имеет тип int ". Синтаксис объявления переменной "подстраивается" под синтаксис выражений, в которых эта переменная может встретиться. Указанный принцип применим и в объявлениях функций. Например, запись

double *dp, atof (char *);

означает, что выражения *dp и atof(s) имеют тип double, а аргумент функции atof есть указатель на char.

Вы, наверное, заметили, что указателю разрешено указывать только на объекты определенного типа. (Существует одно исключение: "указатель на void " может указывать на объекты любого типа, но к такому указателю нельзя применять оператор косвенного доступа)

Если ip указывает на x целочисленного типа, то *ip можно использовать в любом месте, где допустимо применение x; например,

*ip = *ip + 10;

увеличивает *ip на 10.

Унарные операторы * и & имеют более высокий приоритет, чем арифметические операторы, так что присваивание

y = *ip + 1;

берет то, на что указывает ip, и добавляет к нему 1, а результат присваивает переменной y. Аналогично

*ip += 1;

увеличивает на единицу то, на что указывает ip; те же действия выполняют

++*ip;

и

(*iр)++;

В последней записи скобки необходимы, поскольку если их не будет, увеличится значение самого указателя, а не то, на что он указывает. Это обусловлено тем, что унарные операторы * и ++ имеют одинаковый приоритет и порядок выполнения - справа налево.

И наконец, так как указатели сами являются переменными, в тексте они могут встречаться и без оператора косвенного доступа. Например, если iq есть другой указатель на int, то

iq = ip;

копирует содержимое ip в iq, чтобы ip и iq указывали на один и тот же объект.

Указатели и аргументы функций

Поскольку в Си функции в качестве своих аргументов получают значения параметров, нет прямой возможности, находясь в вызванной функции, изменить переменную вызывающей функции. В программе сортировки нам понадобилась функция swap, меняющая местами два неупорядоченных элемента. Однако недостаточно написать

swap(a, b);

где функция swap определена следующим образом:

void swap(int х, int у) /* НЕВЕРНО */

{

int temp;

temp = х;

x = y;

у = temp;

}

Поскольку swap получает лишь копии переменных a и b, она не может повлиять на переменные a и b той программы, которая к ней обратилась. Чтобы получить желаемый эффект, вызывающей программе надо передать указатели на те значения, которые должны быть изменены:

swap(&a, &b);

Так как оператор & получает адрес переменной, &a есть указатель на a. В самой же функции swap параметры должны быть объявлены как указатели, при этом доступ к значениям параметров будет осуществляться косвенно.

void swap(int *px, int *py) /* перестановка *px и *py */

{

int temp;

temp = *рх;

*рх = *py;

*ру = temp;

}

Графически это выглядит следующим образом: в вызывающей программе:

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

 




<== предыдущая лекция | следующая лекция ==>
Прямой и обратный факторный анализ. | Завдання педагога полягає в тому, щоб підготувати дитину до вибору, спрямувати її душевні потяги на досягнення високих цілей.

Дата добавления: 2015-08-12; просмотров: 694. Нарушение авторских прав; Мы поможем в написании вашей работы!



Картограммы и картодиаграммы Картограммы и картодиаграммы применяются для изображения географической характеристики изучаемых явлений...

Практические расчеты на срез и смятие При изучении темы обратите внимание на основные расчетные предпосылки и условности расчета...

Функция спроса населения на данный товар Функция спроса населения на данный товар: Qd=7-Р. Функция предложения: Qs= -5+2Р,где...

Аальтернативная стоимость. Кривая производственных возможностей В экономике Буридании есть 100 ед. труда с производительностью 4 м ткани или 2 кг мяса...

Патристика и схоластика как этап в средневековой философии Основной задачей теологии является толкование Священного писания, доказательство существования Бога и формулировка догматов Церкви...

Основные симптомы при заболеваниях органов кровообращения При болезнях органов кровообращения больные могут предъявлять различные жалобы: боли в области сердца и за грудиной, одышка, сердцебиение, перебои в сердце, удушье, отеки, цианоз головная боль, увеличение печени, слабость...

Вопрос 1. Коллективные средства защиты: вентиляция, освещение, защита от шума и вибрации Коллективные средства защиты: вентиляция, освещение, защита от шума и вибрации К коллективным средствам защиты относятся: вентиляция, отопление, освещение, защита от шума и вибрации...

Функциональные обязанности медсестры отделения реанимации · Медсестра отделения реанимации обязана осуществлять лечебно-профилактический и гигиенический уход за пациентами...

Определение трудоемкости работ и затрат машинного времени На основании ведомости объемов работ по объекту и норм времени ГЭСН составляется ведомость подсчёта трудоёмкости, затрат машинного времени, потребности в конструкциях, изделиях и материалах (табл...

Гидравлический расчёт трубопроводов Пример 3.4. Вентиляционная труба d=0,1м (100 мм) имеет длину l=100 м. Определить давление, которое должен развивать вентилятор, если расход воздуха, подаваемый по трубе, . Давление на выходе . Местных сопротивлений по пути не имеется. Температура...

Studopedia.info - Студопедия - 2014-2024 год . (0.012 сек.) русская версия | украинская версия