Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Использование ссылочного типа




Использование встроенного отладчика.

Адрес в качестве возвращаемого значения функции.

Массивы указателей на строки.

Дополнительная информация об указателях на указатели.

Массивы указателей.

Строки (массивы типа char).

Использование указателей при работе с массивами.

Функции, массивы и указатели.

Использование указателей типа void.

Использование функции sizeof() с указателями в среде DOS.

Переносимость указателей.

Сравнение указателей.

Операции с указателями.

Арифметические операции с указателями и массивы.

Арифметические операции с указателями.

Указатели на строки.

Указатели на указатели.

Указатели и многомерные массивы.

Указатели на массивы.

Неправильное использование операции определения адреса.

Инициализация указателей.

Простые операторы с указателями.

13.1.11. Сложности при использовании операций ++ и --.

13.2. Указатели на функции.

13.3. Динамическая память.

13.4. Указатели и массивы.

13.5. Ссылочный тип в C++ (reference type).

 

9. Указатели.

7.20. Определение переменных-указателей.

Иной, зачастую более удобный и эффективный способ доступа к переменной, — использовать другую переменную, содержащую адрес той, которая вам необходима. Предположим, к примеру, что имеются целая переменная imemorycell_contents ("содержимое ячейки памяти") и переменная pimemorycell_address ("адрес ячейки памяти"), в которой может храниться адрес первой переменной (имена произвольные, но весьма символические). Как вы уже видели, в С перед именем переменной можно использовать операцию определения адреса (&); при этом возвращается не значение переменной, а ее адрес. Следовательно, вас не удивит синтаксис выражения для присвоения адреса некоторой переменной другой, в которой этот адрес хранится:

 

pimemorycell_address = &imemorycell_contents;

 

 

 

Рис. 13.1. Пример переменной-указателя

 

 

Рис. 13.2. Переменная pimemorycell_address указывает на imemorycell_contents

 

Переменная, в которой хранится адрес, например pimemorycell_address, называется переменной-указателем или просто указателем (pointer). Это отношение между переменными показано на рис. 13.1.

Переменная imemorycell_contents была помещена в память с адресом 7751. После выполнения приведенного выше оператора адрес переменной imemorycell_contents присваивается указателю pimemorycell_address. Это описывается следующим образом: pimemorycell_address указывает на imemorycell_contents, что иллюстрируется на рис. 13.2. Стрелка направлена из ячейки, содержащей адрес, к ячейке, адрес которой хранится.

Для обращения к содержимому ячейки, адрес которой хранится в pimemorycell_address, достаточно перед указателем поставить звездочку: *pimemorycell_address. Это действие называется разыменованием (dereference) указателя pimemorycell_address. Если, к примеру, выполнить два следующих оператора, то значение переменной imemorycell_contents будет равно 20. (см. рис. 13.3).

 

 

Рис. 13.3. Использование указателя в операторе присваивания

 

pimemorycell_address = &imemorycell_contents;

*pimemorycell_address = 20;

 

Звездочку (*) можно рассматривать как директиву поиска адресуемой ячейки памяти согласно стрелке на рис. 13.3. Заметьте, что если указатель pimemorycell_address содержит адрес переменной imemorycell_contents, то следующие два оператора будут эквивалентны; они оба записывают значение 20 в imemorycell_contents:

 

imemorycell_contents = 20;

*pimemorycell_address = 20;

 

7.20.1. Объявление переменных-указателей.

В языке С, также как и во многих других языках, необходимо описывать каждую переменную. Для описания переменной-указателя pimemorycell_address, которая содержит адрес некоторой переменной типа int, нужно написать:

 

int *pimemorycell_address;

 

 

7.20.2. Простые операторы с указателями.

В следующем фрагменте программы выполняется обмен содержимым переменных iresult_a и iresult_b; для этого используются операции определения адреса и разыменования:

 

int iresult_a = 15, iresult_b = 37, itemporary;

int *piresult;

 

piresult = &iresult_a;

itemporary = *piresult;

*piresult = iresult_b;

iresult_b = itemporary;

 

Первая строка программы — это стандартные описание и инициализация переменных: выделяется три ячейки памяти для целых чисел, каждой ячейке присваивается имя, и две переменные инициализируются

Во втором операторе программы переменная piresult описывается как указатель на тип int. Этот оператор выделяет ячейку памяти и присваивает ей имя (переменная располагается по адресу 1920). Если попытаться в программе использовать piresult, то компилятор не выдаст никаких предупреждений, и ссылка будет на неинициализированное содержимое переменной. В четвертом операторе указателю piresult присваивается адрес переменной iresult_a

Следующий оператор программы для доступа к содержимому ячейки, на которую указывает piresult, то есть iresult_a, использует выражение *piresult.

itemporary = *piresult;

Следовательно, в переменную itemporary записывается целое число 15.

В пятом операторе программы содержимое переменной iresult_b копируется в ячейку, адрес которой хранится в piresult.

*piresult = iresult_b;

Последний оператор в программе просто копирует содержимое одной целочисленной переменной itemporary в другую такую же переменную iresult_b.

 

7.20.3. Инициализация указателей.

Как и многие другие переменные С, указатели можно инициализировать при их описании. Например: следующие два оператора выделяют память для двух переменных iresult и piresult.

 

int iresult;

int *piresult = &iresult;

 

Переменная iresult — это обычная целая переменная, a piresult — указатель на целое число. Кроме этого, указатель piresult при инициализации получает значение адреса переменной iresult. Будьте осторожны! Эта запись может в чем-то запутать: инициализируется не значение *piresult (которое должно быть целым числом), а указатель piresult (который является адресом целой переменной). Второй из приведенных выше операторов можно записать при помощи двух следующих эквивалентных операторов:

 

int *piresult;

piresult = &iresult;

 

 

7.20.4. Неправильное использование операции определения адреса.

Операцию определения адреса можно использовать не во всех выражениях С. В следующих примерах показаны ситуации, в которых эту операцию (&) применять нельзя:

 

/*нельзя использовать вместе с константами*/

pivariable = &48;

 

/*нельзя использовать в выражениях с арифметическими операциями*/

int iresult = 5;

pivariable = &(iresult + 15);

 

/*нельзя использовать вместе с регистровыми переменными*/

register register1;

pivariable = &register1;

 

 

7.20.5. Указатели на массивы.

Как уже упоминалось, указатели и массивы связаны между собой. Известно, что имя массива представляет собой константу, значение которой равно адресу первого элемента массива. Поэтому имя массива нельзя менять при помощи операции присваивания или любой другой операции. В следующем примере при объявлении данных имя массива ftemperatures представляет собой константу, которая равна адресу первого элемента массива из 20 чисел с плавающей точкой:

 

#define IMAXREADINGS 20

float ftemperatures[IMAXREADINGS];

float *pftemp;

 

В следующем операторе адрес первого элемента массива присваивается указателю pftemp:

 

pftemp = ftemperatures;

 

Эквивалентный оператор выглядит так:

 

pftemp = &ftemperatures [0];

 

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

 

ftemperatures = pftemp;

&ftemperatures[0] = pftemp;

 

В этих операторах делается попытка присвоить некоторое значение константе ftemperatures или ее эквиваленту ftemperatures[0], что имеет смысла не больше, чем выражение:

 

10 = pftemp;

 

 

1.20.5.1. Указатели и многомерные массивы.

Как создать указатели для многомерных массивов? Чтобы найти ответ на этот вопрос, рассмотрим несколько примеров.

Предположим, что у нас есть описания

 

/*указатель на целый тип */

 

Тогда на что

 

pri = zippo;

 

указывает? На первый столбец первой строки:

 

zippo == &zippo[0][0]

 

А на что указывает pri + 1? На zippo[0][1], т. е. на 1-ю строку 2-го столбца? Или на zippo [1][0], элемент, находящийся во второй строке первого столбца? Чтобы ответить на поставленный вопрос, нужно знать, как располагается в памяти двумерный массив. Он размещается, подобно одномерным массивам, занимая последовательные ячейки памяти. Порядок элементов определяется тем, что самый правый индекс массива изменяется первым, т. е. элементы массива располагаются следующим образом:

 

zippo[0][0] zippo[0][1] zippo[1][0] zippo[1][1] zippo[2][0]

 

Сначала запоминается первая строка, за ней вторая, затем третья и т. д. Таким образом, в нашем примере:

 

pri == &zippo[0][0] /* 1-я строка, 1 столбец */

pri + 1 == &zippo[0][1] /* 1-я строка, 2 столбец */

pri + 2 == &zippo[1][0] /* 2-я строка, 1 столбец */

pri + 3 == &zippo[1][1] /* 2-я строка, 2 столбец */

 

Получилось? Хорошо, а на что указывает pri + 5? Правильно, на

 

zippo[2][1].

 

 

7.20.6. Указатели на строки.

Некоторая строковая константа, например "File not ready", на самом деле хранится в виде массива символов, последним элементом которого является null-символ (см. рис. 13.4). Поскольку указатель типа char может хранить адрес символа, такой указатель можно описать и инициализировать. Например:

 

char *psz = "File not ready"

 

 

 

Рис. 13.4. Строка с null-символом в памяти компьютера

 

В этом операторе описан указатель psz типа char, и ему присвоено начальное значение — адрес первого символа строки (см. рис. 13.5). Кроме этого, выделена память для самой строки. Этот оператор можно было бы записать следующим образом:

 

char *psz;

psz = "File not ready";

 

 

Пис.13.5. Инициализация указателя на строку

 

Опять-таки, нужно помнить о том, что адрес присвоен переменной psz, a не *psz, ссылающейся на символ "Р. Приведенный выше пример помогает прояснить этот момент, поскольку в нем используются отдельные операторы для описания и для инициализации указателя.

Следующий пример иллюстрирует принципиальное различие при работе с указателями на строки и указателями на массивы символов:

 

char *psz = "File not ready";

char pszarrayf = "Drive not ready";

 

Главное отличие между двумя этими операторами заключается в том, что значение psz можно изменить (поскольку это — указатель-переменная), значение pszarray изменить нельзя (поскольку это — указатель-константа). Для достижения аналогичного результата следующий оператор присваивания использовать нельзя:

 

 

7.20.7. Арифметические операции с указателями.

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

 

// 10PTARTH.CPP

// Программа на C++, иллюстрирующая арифметические операции

// с указателями

 

#include "stdafx.h"

#include "E:\LECTURE\AlgorithmProgramming 02\Universal_HederFile.h"

 

void StopWait(void);

 

main()

{

int an_integer, *pi;

float a_real, *pf;

pi=&an_integer;

pf=&a_real;

cout << "pi= " << pi << " pf= " << pf << endl;

pi++;

pf++;

cout << "pi= " << pi << " pf= " << pf << endl;

StopWait(); /* Wait a little */

return (0);

}

 

Предположим, что целое число занимает 2 байта, а число с плавающей точкой — 4 байта. Допустим, что переменная an_integer хранится в ячейке с адресом 2000, а переменная a_real расположена в памяти по адресу 4000. После выполнения последних двух строк программы pi будет содержать адрес 2002, a pf— 4004. Но, спросите вы, разве операция инкремента увеличивает число не на 1? Да, это так для символьных переменных, но не всегда верно для указателей. В данном примере, поскольку переменная pi описана как указатель на целое число (которое в нашей системе занимает 2 байта), то при выполнении операции инкремента происходит проверка типа переменной, а затем выбирается соответствующее значение для самой операции. Для целых чисел это значение равно 2, а для чисел с плавающей точкой — 4 (в нашей системе). Этот принцип соблюдается для любого типа данных, на который ссылается указатель. Если бы указатель ссылался на структуру размером в 20 байт, то операции инкремента или декремента меняли бы текущий адрес, хранящийся в указателе, на 20.

Адрес в указателе можно модифицировать не только при помощи операций ++ и —, но и используя целочисленное сложение и вычитание. Например: при помощи следующего оператора можно сместиться на четыре числа типа float относительно того числа, на которое в настоящий момент ссылается указатель:

 

pf = pf + 4;

 

 

7.20.8. Арифметические операции с указателями и массивы.

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

 

/*10ARYSUB.C

Программа на С, использующая обычные индексы массива*/

#include "stdafx.h"

#include "E:\LECTURE\AlgorithmProgramming 02\Universal_HederFile.h"

 

#define ISIZE 10

void StopWait(void);

 

main()

{

char string10[ISIZE];

int i;

fputs("Enter 11 symbols\n",stdout);

for(i=0; i < ISIZE; i++)

string10[i]=getchar();

for(i=ISIZE-1;i >+ 0; i--)

putchar(string10[i]);

fputs("\n",stdout);

 

StopWait(); /* Wait a little */

return (0);

}

 

Вот второй пример:

 

/*10ARYPTR.C

Программа на С, в которой для доступа к элементам массива используются арифметические

операции с указателями.*/

 

#include "stdafx.h"

#include "E:\LECTURE\AlgorithmProgramming 02\Universal_HederFile.h"

 

#define ISIZE 10

void StopWait(void);

 

main()

{

char string10[ISIZE], *pc;

int icount;

pc=&string10[0];

fputs("Enter 11 symbols\n",stdout);

for(icount=0; icount < ISIZE; icount++)

{

*pc=getchar();

pc++;

}

fputs("\n",stdout);

pc=&string10[ISIZE-1];

for(icount=0; icount < ISIZE; icount++)

{

putchar(*pc);

pc--;

}

fputs("\n",stdout);

 

StopWait(); /* Wait a little */

return (0);

}

 

 

7.20.9. Операции с указателями.

/* операции с указателями */

#include<stdio.h>

#define PR(X) printf(" X = %u, *X = %d, &X = %u\n", X, *X, &X)

int urn[]= {100, 200, 300};

main()

 

{int *ptr1, *ptr2;

 

ptr1 = urn;

ptr2 = &urn[2];

PR(ptr1);

ptr1++;

PR(ptr1);

PR(ptr2);

++ptr2;

PR(ptr2);

printf("ptr2 - ptr1 = %u\n", ptr2 - ptr1);

}

 

В результате работы программы получены следующие результаты:

В результате работы программы получены следующие результаты:

 

ptr1 = 18, *ptr1 = 100, &ptr1 = 55990

ptr1 = 20, *ptr1 = 200, &ptr1 = 55990

ptr2 = 22, *ptr2 = 300, &ptr2 = 55992

ptr2 = 24, *ptr2 = 29808, &ptr2 -= 55992

ptr2 – ptr1 = 2

 

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

1. ПРИСВАИВАНИЕ. Указателю можно присвоить адрес. Обычно мы выполняем это действие, используя имя массива или операцию получения адреса (&). Программа присваивает переменной ptr1 адрес начала массива urn; этот адрес принадлежит ячейке памяти с номером 18. (В нашей системе статические переменные запоминаются в ячейках оперативной памяти.) Переменная ptr2 получает адрес третьего и последнего элемента массива, т. е. urn [2].

2. ОПРЕДЕЛЕНИЕ ЗНАЧЕНИЯ. Операция * выдает значение, хранящееся в указанной ячейке. Поэтому результатом операции *ptr1 в самом начале работы программы является число 100, находящееся в ячейке с номером 18.

3. ПОЛУЧЕНИЕ АДРЕСА УКАЗАТЕЛЯ. Подобно любым переменным переменная типа указатель имеет адрес и значение. Операция & сообщает нам, где находится сам указатель. В нашем примере указатель ptr1 находится в ячейке с номером 55990. Эта ячейка содержит число 18, являющееся адресом начала массива urn.

4. УВЕЛИЧЕНИЕ УКАЗАТЕЛЯ. Мы можем выполнять это действие с помощью обычной операции сложения либо с помощью операции увеличения. Увеличивая указатель, мы перемещаем его на следующий элемент массива. Поэтому операция ptr1++ увеличивает числовое значение переменной ptr1 на 2 (два байта на каждый элемент массива целых чисел), после чего указатель ptr1 ссылается уже на urn[1] (смотри рисунок). Теперь ptr1 имеет значение 20 (адрес следующего элемента массива), а операция *ptr1 выдает число 200, являющееся значением элемента urn[1]. Заметим, что адрес самой ячейки ptr1 остается неизменным, т. е. 55990. После выполнения операции сама переменная не переместилась, потому что она только изменила значение!

 

Для переменных и констант типа указатель можно использовать простое сложение:

 




Поделиться с друзьями:


Дата добавления: 2015-01-03; Просмотров: 1053; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




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