Студопедия

КАТЕГОРИИ:


Архитектура-(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)

Правильно Неправильно




ptr1 ++; urn++;

х++; 3++;

ptr2 = ptr1 + 2; ptr2 = urn++;

ptr2 = urn + 1; x = у + 3++;

 

5. РАЗНОСТЬ. Можно находить разность двух указателей. Обычно это делается для указателей, ссылающихся на элементы одного и того же массива, чтобы определить, на каком расстоянии друг от друга находятся элементы. Помните, что результат имеет тот же тип, что и переменная, содержащая размер массива.

 

 

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

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

 

*pc++=getchar();

*++pc=getchar();

 

В первом операторе символ, полученный функцией getchar(), запоминается в текущей ячейке, на которую указывает рс, а затем рс инкрементируется. Во втором операторе сначала инкрементируется адрес, хранящийся в рс, а затем символ, полученный от функции, запоминается в той ячейке, на которую указывает обновленный адрес. Далее вы увидите, как эти два различных типа присваивания значений указателям используются для обращения к элементам списка argv.

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

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

· Вычитание целого числа из указателя

· Вычитание одного указателя из другого (обычно они указывают на один и тот же объект)

· Сравнение указателей при помощи операций отношения, например, = или >=

Указатели одинаковых типов (то есть указатели, которые ссылаются на один и тот же тип данных) можно также сравнивать между собой. Полученный результат — ИСТИНА (!0) или ЛОЖЬ (0) — можно или анализировать, или присвоить некоторой целой переменной — так же, как и результат любого другого логического выражения.

 

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

В приведенных примерах адреса представлялись целыми числами. Вы можете предположить, что в С указатели имеют тип int. Это не так. Указатель содержит адрес переменной некоторого типа, однако сам указатель не относится ни какому простому типу данных вроде int, float и им подобному. В конкретной системе С указатель может копироваться в переменную типа int, а переменная int может копироваться в указатель; однако, язык С не гарантирует, что указатели могут храниться в переменных типа int. Для обеспечения переносимости программного кода таких операций следует избегать.

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

 

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

В этой программе на C++ печатается размер указателей по умолчанию, и их размер с моделями памяти __far и near. Также используется директива препроцессора (#), определяющая PRINT_SIZEOF с аргументом A_POINTER, поэтому печатается не только размер указателя, но и его название.

 

//10STRIZE.CPP

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

#include <stdio.h>

#define PRINT_SIZEOF(A_POINTER) \

printf("sizeof\t("#A_POINTER")\t= %d\n", \

sizeof(A_POINTER))

main()

{

char *reg_pc;

long double *reg_pldbl;

char __far *far_pc;

long double __far *far_pldbl;

char __near *near_pc;

long double __near *near_pldbl;

PRINT_SIZEOF(reg_pc);

PRINT_SIZEOF(reg_pldbl);

PRINT_SIZEOF(far_pc);

PRINT_SIZEOF(far_pldbl);

PRINT_SIZEOF(near_pc);

PRINT_SIZEOF(near_pldbl);

return (0);

}

 

Результат выполнения программы:

 

 

Sizeof (reg_pc) = 2
Sizeof (reg_pldbl) = 2
Sizeof (far_pc) = 4
Sizeof (far_pldbl) = 4
Sizeof (near_pc) = 2
Sizeof (near_pldbl) = 2

 

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

Указатель на функцию можно использовать в нескольких важных случаях. Рассмотрим, например, функцию qsort(). Одним из ее параметров является указатель на функцию. Адресуемая функция осуществляет необходимое сравнение, которое должно выполняться для элементов сортируемого массива. Применение в qsort() указателя на функцию вызвано тем, что процесс сравнения двух элементов может быть весьма сложным, и при помощи одного управляющего флага его представить нельзя. Функцию нельзя передать по значению — то есть, передать ее код. Однако, в С можно передать указатель на код или указатель на функцию.

Следующая ниже программа на С показывает, как описать указатель на функцию и как передать пользовательскую функцию в функцию qsort(), объявленную в файле stdlib.h.

Вот программа на С:

 

/*10FNCPTR.C

Программа на С, иллюстрирующая объявление пользовательской функции*/

 

#include "stdafx.h"

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

 

#define IMAXVALUES 10

int icompare_funct(const void *iresult_a, const void *iresult_b);

int (*ifunct_ptr)(const void *,const void *);

void StopWait(void);

 

main()

{

int i;

int iarray[IMAXVALUES]={0,5,3,2,8,7,9,1,4,6};

ifunct_ptr=icompare_funct;

qsort(iarray,IMAXVALUES, sizeof(int), ifunct_ptr);

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

printf("%d ",iarray[i]);

printf("\n");

 

StopWait(); /* Wait a little */

return (0);

}

 

int icompare_funct(const void *iresult_a, const void *iresult_b)

{

return((*(int *)iresult_a)-(*(int *) iresult_b));

}

 

 

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

При компиляции программы на С память компьютера разбивается на четыре области, содержащие код программы, все глобальные данные, стек и динамически распределяемую область памяти (иногда ее называют heap-куча). Динамическая память (heap) — это просто свободная область памяти, с которой работают при помощи функций динамического выделения памяти malloc() и free().

При вызове функции malloc() выделяется непрерывный блок памяти для указанного объекта и возвращается указатель на начало этого блока. Функция free() возвращает выделенный блок обратно в динамическую область памяти для повторного использования.

Аргумент, передаваемый функции malloc(), представляет собой объем необходимой памяти в байтах. В следующем фрагменте кода выделяется память для 300 чисел типа float:

 

float *pf;

int inum_floats = 300;

pf = (float *) malloc(inum_floats * sizeof(float));

 

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

 

free((void *) pf);

 

Программы на С и C++ могут в любой момент выделять и освобождать динамическую память. Важно помнить, что на переменные, память для которых выделена в динамической области, не распространяются правила области действия, которые справедливы для других переменных. Эти переменные действуют всегда; поэтому, если вы выделили динамическую область памяти, то не должны забывать о ее освобождении. Если повторно выделить динамическую память, предварительно не освободив ее, то выполнение программы, скорее всего закончится крахом.

Для распределения динамической памяти в большинстве компиляторов С используются описанные выше библиотечные функции malloc() и free(), однако, в C++ эти средства считаются настолько важными, что они включены в ядро языка. В C++ для выделения и освобождения динамической памяти используются операторы new и delete.

 

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

Теперь, когда вы уже глубоко понимаете природу переменных-указателей, становится понятной необходимость указателей типа void. Суть указателя в том, что это переменная, в которой хранится адрес другой переменной. Если бы вы всегда знали размер указателя, то на этапе компиляции не нужно было бы определять его тип. Тогда можно было бы передать в функцию адрес данных любого типа. Затем функция могла бы преобразовать этот адрес в указатель нужного типа (на основании другой информации) и выполнить операции, определяющие результат. Этот процесс позволил бы создавать функции, работающие со множеством различных типов данных.

Следующая программа на C++ иллюстрирует использование указателей void:

 

// 10VOIDPT.CPP

// Программа на C++, использующая указатели void

 

#include "stdafx.h"

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

 

#define ISTRING_MAX 50

void voutput(void *pobject, char cflag);

void StopWait(void);

 

void main()

{

int *pi;

char *psz;

float *pf;

char cresponse, cnewline;

cout << "Please enter the dynamic data type\n";

cout << "you woud like to create.\n\n";

cout << "Use (s)tring, (i)nt or (f)loat";

cout << endl;

cin >> cresponse;

cin.get(cnewline);

switch(cresponse)

{

case 's':

psz=new char[ISTRING_MAX];

cout << "\nPliase enter a string: ";

cin.get(psz,ISTRING_MAX);

voutput(psz, cresponse);

break;

case 'i':

pi=new int;

cout << "\nPliase enter an integer: ";

cin >> *pi;

voutput(pi, cresponse);

break;

case 'f':

pf=new float;

cout << "\nPliase enter a float: ";

cin >> *pf;

voutput(pf, cresponse);

break;

default:

cout << "\n\nObject type not implemented!";

}

 

StopWait(); /* Wait a little */

}

 

void voutput(void *pobject, char cflag)

{

switch(cflag)

{

cout << "\n\nThe string read in: " << (char *) pobject;

delete pobject;

break;

case 'i':

cout << "\n\nThe integer read in: " << *((int *) pobject);

delete pobject;

break;

case 'f':

cout << "\n\nThe float read in: " << *((float *) pobject);

delete pobject;

break;

}

cout << endl;

}

 

 

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

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

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

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

 

/* прибавление к указателю */

#include<stdio.h>

main ()

{

int dates[4], *pti, index;

float bills [4], *ptf;

pti = dates; /* присваивает адрес указателю массива */

ptf = bills;

for (index = 0; index < 4; index++)

printf(“ ukazatel + %d: %10 u %10u\n", index, pti + index, ptf + index);

}

 

Результат работы программы

Вот результат

 

указатели + 0: 56014 56026

указатели + 1: 56016 56030

указатели + 2: 56018 56034

указатели + 3: 56020 56038

 

Первая напечатанная строка содержит начальные адреса двух массивов, а следующая строка — результат прибавления единицы к адресу и т. д. Почему так получается?

 

56014 + 1 = 56016?

56026 + 1 = 56030?

 

В нашей системе единицей адресации является байт, но тип int использует два байта, а тип float — четыре. Что произойдет, если вы скажете: «прибавить единицу к указателю?» Компилятор языка Си добавит единицу памяти. Для массивов это означает, что мы перейдем к адресу следующего элемента, а не следующего байта. Вот почему мы должны специально оговаривать тип объекта, на который ссылается указатель; одного адреса здесь недостаточно, так как машина должна знать, сколько байтов потребуется для запоминания объекта. (Это справедливо также для указателей на скалярные переменные; иными словами, при помощи операции *pt нельзя получить значение.)

 

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

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

Об этом уже говорилось в гл. 10. Сейчас, когда мы познакомились с указателями, можно заняться более глубоким изучением массивов-аргументов. Давайте проанализируем скелет программы, обращая внимание на описания:

 

/* массив-аргумент */

main ()

{

int ages [50]; /* массив из 50 элементов */

convert(ages);

}

 

convert (years)

int years []; /* каков размер массива? */

{

}

 

Очевидно, что массив ages состоит из 50 элементов. А что можно сказать о массиве years? Оказывается, в программе нет такого массива. Описатель int years[]; создает не массив, а указатель на него.

Вот вызов нашей функции:

 

convert(ages);

 

ages — аргумент функции convert. Вы помните, что имя ages является указателем на первый элемент массива, состоящего из 50 элементов. Таким образом, оператор вызова функции передает ей указатель, т. е. адрес функции convert (). Это значит, что аргумент функции является указателем, и мы можем написать функцию convert () следующим образом:

 

convert (уears)

int *years;

{

}

 

Действительно, операторы

 

int years [];

int *years;

 

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

 

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

Попробуем написать функцию, использующую массивы, а затем перепишем ее, применяя указатели.

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

 

/* X54.C */

#include <stdio.h>

void main(void)

{

int n1=9,i, *pa1,*pa,n;

int array[] = {9,5,3,1,0,-9,-5,-3,-1};

pa1=array;

int mean(int *pa,int n);

i = mean(pa1,n1);

printf("Average is %d\n",i);

}

int mean(int *pa,int n)

 

{

int index;

long sum;

if(n > 0)

{

for(index = 0, sum = 0; index < n; index++)

sum += *(pa+index);

return((int) (sum/n));

}

else

{

printf("Net massiva\n");

return(0);

}

}

Результат работы программы

Эту программу легко переделать, применяя указатели. Объявим ра указателем на тип int. Затем заменим элемент массива array[index] на соответствующее значение: *(ра + index).

 

/* Использование указателей для нахождения среднего значения

массива n целых чисел */

int mean(pa, n)

int *pa, n;

{

int index;

long sum; /*Если целых слишком много, их можно суммировать в формате long int */

if (n > 0)

{

for (index = 0, sum = 0; index < n: index++)

sum += *(pa + index);

return((int) (sum/n)); /* Возвращает целое */

}

else

{

printf(" Нет массива. \n");

return(0);

}

}

 

 

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

Многие строковые операции в С обычно выполняются с использованием указателей и арифметических операций с указателями для доступа к элементам символьного массива. Это обусловлено тем, что символьные массивы или строки, как правило, обрабатываются строго последовательно. Напоминаем, что все строки в С заканчиваются символом null (\0). Следующая программа на C++ иллюстрирует использование указателей с символьными массивами:

 

// 10CHRARY.CPP

// Программа на C++, печатающая массив символов в обратном порядке

// и использующая указатель на символ и оператор декремента

 

#include "stdafx.h"

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

 

void StopWait(void);

 

void main()

{

char pszpalindrome[]="POOR DAN IN A DROOP";

char *pc;

pc=&pszpalindrome[0]+(strlen(pszpalindrome)-1);

do

{

cout << *pc;

pc--;

} while (pc >= pszpalindrome);

cout << endl;

 

StopWait(); /* Wait a little */

}

 

ПРИМЕЧАНИЕ.

Функция strlen() считает только символы. Она не включает в результат null - символ \0

 

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

В языках С и C++ можно создавать не только простые массивы и указатели. Их можно объединить в очень полезную конструкцию — массив указателей. Массив указателей — это такой массив, элементы которого являются указателями на другие объекты. Эти объекты, в свою очередь, могут быть указателями. Это означает, что можно иметь массив указателей, которые ссылаются на другие указатели.

 

// 10ARGCGV.CPP

// Программа на C++, использующая массив указателей для обработки

// аргументов командной строки argc and argv

 

#include "stdafx.h"

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

 

#define IFIND_SMALLEST 0

#define IFIND_LARGEST 1

 

void StopWait(void);

 

int main(int argc, char *argv[])

{

char *psz;

int ihow_many;

int iwhich_extreme=0;

int irange_boundary=32767;

if(argc < 2)

{

// Вам нужно ввести -S,-s,-L,-l и хотя бы одно число

cout << "\nYou need to enter an -S, -s, -L, -l"

<< " and at least one integer value" << endl;

exit(1);

}

while(--argc > 0 && (*++argv)[0] == '-')

{

for(psz=argv[0]+1; *psz!= '\0'; psz++)

{

switch(*psz)

{

case 's':

case 'S':

iwhich_extreme=IFIND_SMALLEST;

irange_boundary=32767;

break;

case 'l':

case 'L':

iwhich_extreme=IFIND_LARGEST;

irange_boundary=0;

break;

default:

// Неизвестный аргумент

cout << "\n\nUnknow argument! " << *psz << endl;

exit(1);

}

}

}

if(argc == 0)

{

// Введите хотя бы одно число

cout << "Pliase enter at least one number\n";

exit(1);

}

ihow_many=argc;

while(argc--)

{

int present_value;

present_value=atoi(*(argv++));

if(iwhich_extreme == IFIND_LARGEST && present_value > irange_boundary)

irange_boundary=present_value;

if(iwhich_extreme == IFIND_SMALLEST && present_value < irange_boundary)

irange_boundary=present_value;

}

// Самое большее / меньшее из... введенных значений

cout << "The ";

cout << ((iwhich_extreme)? "largest": "smallest");

cout << " of the " << ihow_many << " value(s) input is " << irange_boundary

<< endl;

 

StopWait(); /* Wait a little */

return(0);

}

 

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

 

10argcgv

10argcgv 98

10argcgv 98 21

10argcgv -s 98

10argcgv -S 98 21

10argcgv -I 14

10argcgv -L 14 67

 

Взглянув на функцию main(), вы заметите формальные параметры argc и argv, с которыми познакомились ранее. Напомним, что argc — это целое число, равное количеству отдельных элементов или аргументов, появляющихся в командной строке. Переменная argv ссылается на массив указателей на символьные строки.

 

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

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

 

/*10DBLPTR.C

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

------------

#include "stdafx.h"

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

 

#define IMAXELEMENTS 3

void voutput(int **ppiresult_a, int **ppiresult_b, int **ppiresult_c);

void vassign(int *pivirtual_array[], int *pinewblock);

void StopWait(void);

 

main()

{

int **ppiresult_a, **ppiresult_b, **ppiresult_c;

int *pivirtual_array[IMAXELEMENTS];

int *pinewblock, *pioldblock;

ppiresult_a=&pivirtual_array[0];

ppiresult_b=&pivirtual_array[1];

ppiresult_c=&pivirtual_array[2];

pinewblock=(int *)malloc(IMAXELEMENTS*sizeof(int));

pioldblock=pinewblock;

vassign(pivirtual_array, pinewblock);

**ppiresult_a=7;

**ppiresult_b=10;

**ppiresult_c=15;

voutput(ppiresult_a, ppiresult_b, ppiresult_c);

pinewblock=(int *)malloc(IMAXELEMENTS*sizeof(int));

*pinewblock=**ppiresult_a;

*(pinewblock+1)=**ppiresult_b;

*(pinewblock+2)=**ppiresult_c;

free(pioldblock);

vassign(pivirtual_array, pinewblock);

voutput(ppiresult_a, ppiresult_b, ppiresult_c);

 

StopWait(); /* Wait a little */

return(0);

}

 

void vassign(int *pivirtual_array[], int *pinewblock)

{

pivirtual_array[0]=pinewblock;

pivirtual_array[1]=pinewblock+1;

pivirtual_array[2]=pinewblock+2;

}

 

void voutput(int **ppiresult_a, int **ppiresult_b, int **ppiresult_c)

{

printf("%d\n",**ppiresult_a);

printf("%d\n",**ppiresult_b);

printf("%d\n",**ppiresult_c);

}

 

Эта программа иллюстрирует концепцию переменной-указателя (ppiresult_a, ppiresult_b, ppiresult_c), ссылающихся на постоянный адрес (&pivirtual_array[0], &pivirtual_array[1], &pivirtual_array[2]), содержащий ссылочный адрес на ячейку, содержимое которой может динамически изменяться.

 

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

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

 

/*10AOFPTR.C

Программа на С, демонстрирующая описание и использование массивов

указателей*/

 

#include "stdafx.h"

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

 

#define INUMBER_OF_ERRORS 3

char *pszarray[INUMBER_OF_ERRORS]=

{

"\nFile not available.\n", /* Файл отсутствует */

"\nNot an alpha chaacter.\n", /* He буква */

"\nValue not between 1 and 10.\n" /*He значение от 1 до 10 */

};

FILE *fopen_a_file(char *psz);

char cget_a_char(void);

int iget_an_integer(void);

FILE *pfa_file;

 

void StopWait(void);

 

void main()

{

char cvalue;

int ivalue;

fopen_a_file("input.dat");

cvalue=cget_a_char();

ivalue=iget_an_integer();

 

StopWait(); /* Wait a little */

}

 

FILE *fopen_a_file(char *psz)

{

const ifopen_a_file_error=0;

pfa_file=fopen(psz,"r");

if(!pfa_file)

printf("%s",pszarray[ifopen_a_file_error]);

return(pfa_file);

}

 

char cget_a_char(void)

{

char cvalue;

const icget_a_char_error=1;

printf("\nEnter a character: "); /* Введите символ */

scanf("%c", &cvalue);

if(!isalpha(cvalue))

printf("%s",pszarray[icget_a_char_error]);

return(cvalue);

}

 

int iget_an_integer(void)

{

int ivalue;

const iiget_an_integer=2;

printf("\nEnter an integer between 1 and 10: "); /*Введите целое */

scanf("%d", &ivalue);

if((ivalue < 1) || (ivalue > 10))

printf("%s", pszarray[iiget_an_integer]);

return(ivalue);

}

 

 

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

В языке C++ имеется способ вызова по ссылке, который использовать еще проще, чем указатели. Для начала рассмотрим использование ссылочных переменных в C++. Так же, как и в С, в языке C++ можно объявить обычные переменные или переменные-указатели. В первом случае для данных действительно выделяется память; во втором случае память резервируется для адреса объекта, который будет создан в другое время. В C++ имеется третий тип объявлений — ссылки. Так же, как указатель, ссылочная переменная указывает на положение другой переменной, однако, так же как и в случае обычной переменной, для ссылки не требуется специальной операции разыменования. Синтаксис ссылочной переменной понятен:

 

int iresult_a=5;

int& riresult_a=iresult_a; // правильно

int& riresult b; // неправильно: нет начального значения

 

В этом примере определяется ссылочная переменная riresult_a и присваивается существующей переменной iresult_a. После этого адресуемое значение имеет два имени — iresult_a и riresul_a. Поскольку обе переменные указывают на одну и ту же ячейку памяти, они представляют собой, по сути, одну переменную. Любое присваивание, сделанное по отношению к riresult_a, отражается на iresult_a; справедливо и обратное утверждение. Следовательно, при помощи ссылки можно создать нечто подобное псевдониму (alias) переменной.

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

 

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

Когда функция возвращает адрес либо в виде переменной-указателя, либо в виде ссылки, пользователь получает некоторый адрес в памяти. Он может считать значение, находящееся по этому адресу, и, если тип указателя не объявлен как const, всегда может что-нибудь туда записать. Если функция возвращает адрес, то пользователь получает возможность читать и — в случае указателей, имеющих тип, отличный от const — обновлять локальные данные. Это важно для проектирования приложений.

 

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

Чтобы увидеть работу этой программы на C++ в реальных условиях, можно воспользоваться встроенным отладчиком. Проследим при помощи окна Trace изменения переменной pi.

Что происходит? При вызове функции ifirst_function() для переменной ilocal_to_first в стеке выделяется локальная область памяти, в которую записывается число 11. После этого функция ifirst_function() возвращает адрес этой локальной переменной (очень плохая новость!). Во второй строке функции mata() вызывается функция isecond_function(), которая в свою очередь выделяет локальную область памяти для переменной ilocal_to_second и записывает туда число 44. Почему же оператор printf печатает значение 44, хотя ему при вызове функции inrst_fiinction() был передан адрес переменной ilocaljojirsf?

Фактически происходит следующее. Когда при вызове функции ifirst_function() адрес временной локальной переменной ilocal_to_first был присвоен переменной pi, то этот адрес был сохранен, несмотря на завершение области действия ilocal_to_first. При вызове функции isecond_function(), ей также понадобилась локальная память. Поскольку переменной ilocal_to_first не стало, переменная ilocal_to_second получила ту же локальную.область, что и ее предшественница. Так как pi указывает на ту же занятую ячейку памяти, то становится понятным, почему при печати адресуемой ячейки появляется число 44. Следует быть чрезвычайно внимательным и стараться не возвращать адреса локальных переменных.

 

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

Подводя черту, можно выделить четыре основных причины использования ссылок C++:

· Ссылки упрощают восприятие программы, поскольку можно игнорировать детали процесса передачи параметра.

· Ссылки передают ответственность за передачу аргумента тому программисту, который пишет функцию, а не тому, который ею пользуется.

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

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

14. Использование функций.

14.1. Создание и использование простой функции.

14.2. Прототипы функций.

14.3. Вызов по значению и вызов по ссылке.

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

14.5. Операция косвенной адресации: *.

14.6. Описание указателей.




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


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


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



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




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