Лекция 4. Производные типы. Тип указатель: указатели на объекты.

Лекция 4. Производные типы. Тип указатель: указатели на объекты.

Краткая аннотация лекции.

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

Цель лекции: изучить классификацию производных типов, тип указатель и соотношения между именами, адресами и значениями переменных, научиться использовать указатели в программных кодах на языке C++.

Текст лекции.

В языке С++ разрешено наряду со стандартными использование производных типов, полученных на основе более простых базовых типов. Производные типы можно условно подразделить на две группы:

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

· массивы;

· указатели;

· ссылки;

· перечисления.

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

· классы;

· структуры;

· объединения.

Переименование типов

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

Синтаксис:

typedef Тип НовоеИмяТипа[Размерность];

Например:

typedef unsigned int UNIT;

typedef char Msg[100];

Такое имя можно затем использовать так же, как и стандартное имя типа:

UNIT a,b,c;//переменные типа unsigned int

Msg str[10];//массив из 10 строк по 100 символов

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

Рассмотрим пример (рис.1). Когда компилятор обрабатывает оператор определения переменной, например, int a=10;, то в памяти выделяется участок памяти в соответствии с типом переменной и записывается в этот участок указанное значение (размер типа int не менее 2 байтов и зависит от реализации). Все обращения к переменной a компилятор заменит на адрес области памяти, в которой хранится эта переменная. Операция &a является операцией взятия адреса ее операнда.

 
  Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru

Рис. 1. Адресация в С++

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

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

Указатель не является самостоятельным типом, он всегда связан с каким-то другим типом. Указатели делятся на две категории:

· указатели на объекты;

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

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

Указатели на объекты

В общем случае синтаксис определения указателя на объект:

Тип*Описатель;

При определении указателя специфицируется имя указателя-переменной (в дальнейшем указатель) и тип объекта, на который он ссылается.

Тип задает тип объекта, адрес которого будет содержать определяемая переменная и может соответствовать базовому, пустому (свободному, родовому, то есть типу void), перечислению, структурному типу и типу объединения. Реально указатель на void ни на что не указывает, но обладает способностью указывать на область любого размера после его типизирования каким-либо объектом.

Описатель – это идентификатор, определяющий имя объявляемой переменой типа указатель или конструкция, которая организует непосредственно доступ к памяти. Описателю обязательно должна предшествовать звездочка (*).

Знак '*' является унарной операцией косвенной адресации, его операнд – указатель, а результат – адрес объекта, на который указывает операнд. Адресация является косвенной, так как обращение к области памяти осуществляется не напрямую по адресу (например, 1А2В), а через объект, которому в памяти соответствует определенный участок. Объем памяти, который выделяется для хранения данных, определяется типом данных и моделью памяти. Для приведенной на рисунке 2 модели памяти адресом переменной типа float с именем summa является 0012FF48, адресом переменной типа int с именем date является 0012FF54, адресом переменной типа char с именем ch является 0012FF63.



Машинный адрес 0012FF48 0012FF49 0012FF4A 0012FF4B 0012FF54 0012FF55 0012FF56 0012FF57 0012FF63
байт байт байт байт байт байт байт байт байт
Значение в памяти 2.015*10-6 'G'
Имя summa date ch

Рис. 2. Адресация типов в С++

Примеры определения указателей:

int *P;

/*указатель Р может содержать адрес объекта типа int*/

float *s;

/*указатель s может содержать адрес объекта типа float*/

Синтаксис объявления указателя на объект базового типа:

Тип*ИмяУказателя;

где ИмяУказателя – идентификатор.

Например,

char *s; //переменная s – указатель на объект типа char

double *x; /*переменная х – указатель на объект типа

double, вещественного числа с плавающей

точкой удвоенной точности*/

int *k, *ff; //k, ff – указатели на объекты целого типа

int *p, y; /*р – указатель на объект типа int,

y – целочисленная переменная и не является

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

int x, *p; /*х – целочисленная переменная и не является

указателем,

р – указатель на объект типа int*/

Указатель может быть константой или переменной, а также указывать на константу или переменную.

Например:

int i;//целая переменная

const int ci=1; //целая константа

int *pi; //указатель на целую переменную

const int *pci; //указатель на целую константу

int *const cpi; //указатель-константа на целую переменную

const int *const cpc;

//указатель-константа на целую константу

При объявлении указателя его можно сразу проинициализировать (задать значение):

int *pi=&i; //указатель на целую переменную

const int *pci=&ci; //указатель на целую константу

int *const cpi=&i;

//указатель-константа на целую переменную

const int *const cpc=&ci;

//указатель-константа на целую константу

Если модификатор const относится к указателю (т.е. находится между именем указателя и *), то он запрещает изменение значения указателя, а если он находится слева от типа (т.е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

Способы инициализации указателя

1. Присваивание указателю адреса области памяти существующего объекта:

· Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru с помощью операции получения адреса:

int a=5;

int *p=&a;

· с помощью проинициализированного указателя

int *r=p;

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

char *cp=(char*)0х В800 0000;

где 0х В800 0000 – шестнадцатеричная константа, (char*) – операция приведения типа.

3. Присваивание указателю пустого значения:

int *N=NULL; или int *N=0;

Спецификатор указателя при форматированном выводе

Если на экран необходимо вывести адрес, следует применять спецификатор %p.

%p – спецификатор указателя.

Этот спецификатор формата заставляет функцию printf() выводить на экран адрес, формат которого совместим с типом адресации, принятой в компьютере.

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

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

· разыменование (*) – получение значения величины, адрес которой хранится в указателе;

· взятие адреса (&);

· присваивание;

· арифметические операции

§ сложение указателя только с константой,

§ вычитание: допускается разность указателей и разность указателя и константы,

§ инкремент (++) увеличивает значение указателя на величину sizeof(тип);

§ декремент (--) уменьшает значение указателя на величину sizeof(тип);

· сравнение;

· приведение типов.

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

// Выбор данных из памяти с помощью разных указателей

// Использование функций приведения типов

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

unsigned long L=12345678;

char *cp=(char*)&L;

int *ip=(int*)&L;

long *lp=(long*)&L;

cout <<"\n&L = "<<&L;

cout <<"\nL = "<<L;

cout <<"\n*cp = "<<*cp;

cout <<"\n*ip = "<<*ip;

cout <<"\n*lp = "<<*lp;

system("pause");

return 0;

}

Пример 2.

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

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

int a,c,b;

int *ca, *cb;

int *sa, *sb, *sc;

cout << "a = "; cin >> a;

cout << "b = "; cin >> b;

c=a+b;

sb=&b;//инициализация указателей через взятие адреса

sa=&a;

sc =&c;

//присваивание указателю значения выражения

*sc = c+a-b;

ca=sa;//присваивание указателю значения другого указателя

cb=sb;

*sa=12;//присваивание указателю значения константы

cout << "\n*ca = " << *ca;

cout << "\n*sa = " << *sa;

cout << "\n*cb = " << *cb;

cout << "\n*sb = " << *sb;

cout << "\n*sc = " << *sc;

cout << "\nca = " << ca;

cout << "\ncb = " << cb;

cout << "\nsc = " << sc;

cout << "\na = " << a;

cout << "\nb = " << b;

cout << "\nc = " << c;

cout << "\n&a = " << &a;

cout << "\n&b = " << &b;

cout << "\n&c = " << &c;

cout << "\n*&a = " << *&a;

cout << "\n*cb-*ca = " << *cb-*ca;

cout << "\n*cb+*ca = " << *cb+*ca;

*cb=+2; //сложение с константой

cout << "\ncb = " << cb;

cb++; //инкремент

cout << "\ncb = " << cb;

ca--; //декремент

cout << "\ncа = " << ca;

/*разность указателей - разность их значений, деленная

на размер типа в байтах*/

cout << "\ncb-ca = " << cb-ca;

system("pause");

return 0;

}

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

Пример 3.

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

int x=10;

int y=10;

int *xptr=&x;

int *yptr=&y;

//сравниваем указатели

if (xptr == yptr)

cout << "Указатели равны\n";

else

cout << "Указатели неравны\n";

//сравниваем значения, на которое указывает указатель

if (*xptr == *yptr) {

cout << "Значения равны\n";

} else {

cout << "Значения неравны\n";}

system("pause");

return 0;

}

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

Ключевые термины

Адрес объекта –это адрес области оперативной памяти, по которому хранится объект в соответствии с особенностями представления типа.

Инициализация указателя– это определение значения указателя.

Косвенная адресация– этообращение к области памяти не напрямую, по адресу, а через объект, которому в памяти соответствует определенный участок.

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

Переименование типов– это задание нового имени для существующего типа.

Производные типы данных – это типы, полученные на основе более простых базовых типов.

Разыменование– это операция получения значения объекта, адрес которого хранится в указателе;

Составные производные типы – это типы, являющиеся производными от различных существующих или ранее объявленных типов.

Указатель – это именованный объект, предназначенный для хранения адреса области памяти.

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

Указатель-константа– это указатель, значение которого нельзя изменить в процессе выполнения программы.

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

Краткие итоги

1.В языке С++ производные типы данных классифицируют в зависимости от построения на производные и непосредственно производные.

2.Для экономии памяти и времени, затрачиваемого на обращение к данным, в программах используют указатели на объекты.

3.Указатель не является самостоятельным типом, он всегда связан с другим типом.

4.Указатель может быть константой или переменной, а также указывать на константу или переменную.

5.Указатель типа void указывает на область памяти любого размера. Разыменование такого указателя необходимо проводить с операцией приведения типов.

6.До первого использования в программе объявленный указатель необходимо проинициализировать.

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

8.Над указателями определены операции: разыменование, взятие адреса, декремент, инкремент, увеличение (уменьшение) на целую константу, разность, определение размера.

9.Над указателями определены операции сравнения.

Набор для практики

Вопросы

1. Почему указатель не может существовать как самостоятельный тип?

2. С какой целью в программе может быть использован указатель типа void?

3. Что будет являться результатом разыменования указателя типа void без приведения типов?

4. Как изменится значение указателя после применения к нему операции инкремента (декремента)?

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

6. В чем отличие указателя на константу от указателя-константы?

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

8. Если объект занимает в памяти несколько байтов, то какой адрес является значением указателя на этот объект?

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

Упражнения

1.Наберите коды программ из Примеров 1-3. Выполните компиляцию и запуск программ.

2.В программе определите и инициализируйте переменную типа double, указатель double * и указатель типа void *. Присвойте указателям адрес переменной. Напечатайте адрес переменной, значения указателей и значения, получаемые при разыменовании указателей. Чтобы продемонстрировать роли и последовательность выполнения унарных операций получения адреса & и разыменования *, выведите на печать значение выражения *&имя_переменной.

3.Задано натуральное число. Разместите в памяти последовательно все его цифры, используя указатели и операции над ними.

4.Определите и инициализируйте переменную типа double. Определите указатели char *, int *, double *, void *, инициализируйте их адресом переменной. Напечатайте значения указателей, их размеры и длины участков памяти, которые связаны с выражениями, разыменовывающими указатели.

Литература

1. Керниган, Б. Язык программирования Си / Б. Керниган, Д. Ритчи. – М.: Вильямс, 2007. – 304 с.

2. Подбельский, В.В. Практикум по программированию на языке Си: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2004. – 576 с.

3. Подбельский, В.В. Программирование на языке Си: учеб. пособие / В.В. Подбельский, С.С. Фомин. – М.: Финансы и статистика, 2004. – 600 с.

4. Подбельский, В.В. Язык Си++: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2005. – 560 с.

5. Романов, Е.Л. Практикум по программированию на языке С++: учеб. пособие / Е.Л. Романов. – СПб: БХВ-Петербург, 2004. – 432 с.

6. С/С++. Структурное программирование: практикум / Т.А. Павловская, Ю.А. Щупак. – СПб: Питер, 2004. – 239 с.

Краткая аннотация лекции.

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

Цель лекции: изучить указатели на функции и методы передачи функций как параметров, научиться использовать указатели на функции в программных кодах на языке C++.

Текст лекции.

Функции, как и элементы данных, имеют адреса. Адресом функции является адрес памяти, с которого начинается машинный код функции.

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

· принять адрес функции;

· объявить указатель на функцию;

· использовать указатель на функцию для вызова этой функции.

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

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

Синтаксис определения указателя на функцию:

тип_функции(*имя_указателя)(спецификация_параметров)

где тип_функции – определяет тип возвращаемого функцией значения;

имя_указателя – идентификатор;

спецификация_параметров – определяет состав и типы параметров функции.

Например:

int (*pf)(); // без контроля параметров вызова

int (*pf)(void); // без параметров, с контролем по прототипу

int (*pf)(int, char*); // с контролем по прототипу

В соответствии с принципом контекстного определения типа данных эту конструкцию следует понимать так: pf – переменная, при косвенном обращении к которой вызывается функция с соответствующим прототипом, например int_F(int, char*), то есть pf содержит адрес функции или указатель на функцию. Следует обратить внимание на то, что в определении указателя присутствует прототип – указатель ссылается не на произвольную функцию, а только на одну из функций с заданной схемой формальных параметров и результата.

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

Например,

int (*func1Prt)(char);

задает определение указателя func1Prt на функцию с параметром типа char, возвращающую значение типа int.

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

int *func(char);

это не определение указателя, а объявление (прототип) функции c именем func и параметром типа char, возвращающей значение указателя типа int *. В этом случае указатель указывает на значение функции.

Если же выполнить объявление:

char *(*func2Prt)(char *,int);

то определение указателя func2Prt на функцию с параметрами типа указатель на char и типа int, возвращающую значение типа указатель на char.

Синтаксис вызова функции с помощью указателя:

(*имя_указателя)(список_фактических_параметров);

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

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

Указатели на функции в основном используются в следующих случаях.

· Многие библиотечные функции в качестве аргумента получают указатель на функцию. Например, функция сортировки qsort() получает четвертым аргументом указатель на составляемую пользователем функцию сравнения сортируемых элементов.

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

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

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

Пример 1.

//Определение и использование указателей на функции

#include "stdafx.h"

#include <iostream>

using namespace std;

//Определение и использование указателей на функции

void f1(); //объявление (прототип)функции f1

void f2(); //объявление (прототип)функции f2

int _tmain(int argc, _TCHAR* argv[]) {

void (*ptr)(); //ptr - указатель на функцию

f2(); //явный вызов функции f2

ptr=f2;//указателю присваивается адрес функции f2

(*ptr)();

//вызов функции f2 по ее адресу с разыменованием указателя

ptr=f1;//указателю присваивается адрес функции f1

(*ptr)();

//вызов функции f1 по ее адресу с разыменованием указателя

ptr(); // вызов функции f1 без разыменованием указателя

system("pause");

return 0;

}

//описание функции f1 и f2

void f1() {

cout << "Выполняется f1\n";

}

void f2() {

cout << "Выполняется f2\n";

}

Пример 2.

//Вариант 1 использования указателя на функцию

#include "stdafx.h"

#include <iostream>

using namespace std;

float plus(float, float); //Объявление (прототип) функции

int _tmain(int argc, _TCHAR* argv[]){

float x=2.1, y=4.89;

float (*func)(float,float);

//определение указателя func на функцию

printf("Сумма равна %.3f\n",plus(x,y));

func=plus;

//указателю присвоить адрес func точки входа в функцию plus

printf("(Используем указатель на функцию)

Сумма = %.3f\n",func(x,y));

system("pause");

return 0;

}

//Описание функции сложения двух аргументов

float plus(float a, float b) {

return a+b;

}

//Вариант 2 использования указателя на функцию

#include "stdafx.h"

#include <iostream>

using namespace std;

float plus(float, float); //Объявление (прототип)функции

int _tmain(int argc, _TCHAR* argv[]){

float x=2.1, y=4.89;

float (*func)(float, float)=&plus;

//определение указателя на функцию plus

printf("Сумма равна %.3f\n",plus(x,y));

func=plus;

//указателю присвоить адрес точки входа в функцию plus

printf("(Используем указатель на функцию)

Сумма = %.3f\n",func(x,y));

system("pause");

return 0;

}

//Описание функции сложения двух аргументов

float plus(float a, float b) {

return a+b;

}

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

Пример 3: Вычислите приближенное значение интегралов с помощью формулы прямоугольников, задав пределы интегрирования [a, b] и число интервалов разбиения (N): Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru и Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru .

#include "stdafx.h"

#include <iostream>

using namespace std;

//Объявление (прототипы) функций:

/*функция rectangle() возвращает значение типа double, ее параметры:*/

/*pf–указатель на функцию с параметром типа double, возвращающую значение double*/

/*a, b – пределы интегрирования, величины типа double*/

double rectangle(double(*pf)(double), double a, double b);

/*функция ratio() возвращает значение типа double, ее параметр типа double*/

double ratio(double x);

/*функция cos4_2() возвращает значение типа double, ее параметр типа double*/

double cos4_2(double v);

int _tmain(int argc, _TCHAR* argv[]){

double a,b,c;

printf("\nВведите значения пределов интегрирования:");

printf("\na= ");

scanf("%lf",&a);

printf("\nb= ");

scanf("%lf",&b);

c=rectangle(ratio,a,b);

printf("Первый интеграл = %f\n",c);

printf("Второй интеграл = %f\n",rectangle(cos4_2,a,b));

system("pause");

return 0;

}

double rectangle(double(*pf)(double), double a, double b){

/*Вычисление определенного интеграла с помощью формулы прямоугольников*/

int N, i;

double h,s=0.0;

printf("\nВведите количество интервалов разбиения: N= ");

scanf("%d",&N);

printf("\na= ");

h=(b-a)/N; //Длина интервала разбиения

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

s+=pf(a+h/2+i*h);

return h*s;

}

double ratio(double x) { //Подынтегральная функция

double z; //Вспомогательная переменная

z=x*x+1;

return x/(z*z);

}

double cos4_2(double v){ //Подынтегральная функция

double w; //Вспомогательная переменная

w=cos(v);

return 4*w*w;

}

Ключевые термины

Адрес функции –это адрес памяти, с которого начинается машинный код функции.

Косвенное обращение по указателю на функцию – это вызов функции, адресуемой указателем.

Указатели на функции как параметры функции –это параметры функции, типы которых определены как указатели на функции.

Указатель на функцию– это переменная, которая содержит адрес этой функции.

Краткие итоги

1.Адресация функций осуществляется по первому байту расположения машинного кода функции в памяти.

2.Инициализированный указатель на функцию содержит адрес этой функции.

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

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

5.При описании указателя на функцию в качестве параметров можно указывать пустой список или список типов параметров.

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

7.Функции можно передавать в качестве параметров функций.

8.Указатели на функции и указатели как параметры функции имеют широкое практическое применение в программировании.

Набор для практики

Вопросы

1. Почему запрещены арифметические операции над указателями на функции?

2. Почему в описании указателя на функцию необходимы круглые скобки при имени указателя?

3. Может ли функция возвращать значение типа указатель? Если да, то как объявляется прототип такой функции?

4. Могут ли параметрами функции быть указатели на объекты? Если да, то как происходит передача фактических параметров при вызове функции?

5. Могут ли параметрами функции быть указатели на функции? Если да, то как происходит передача фактических параметров при вызове функции?

6. Как понимается следующее объявление:

float *(*func)(int(*pf)(char),float);?

7. В чем отличие результатов вызова функции через указатель с последующим разыменованием указателя и без разыменования указателя?

Упражнения

1.Наберите коды программ из Примеров 1-3. Выполните компиляцию и запуск программ.

2.Методом половинного деления решите уравнения на отрезке [a, b] с данной точностью е: Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru , Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru . При решении считать, что на данном отрезке существует единственный корень. Решите первое уравнение на отрезке [0, 1], а второе на отрезке [100, 150].

3.Решите задачу, используя один указатель на функцию. Разработайте четыре функции над двумя целыми параметрами, соответствующие арифметическим операциям (+, -, *, /). В основной программе задавайте два целых параметра и символьный знак операции до тех пор, пока не будет введен пробел в качестве знака операции. В выходных данных выводите значения функций.

4.Вычислите суммы с данной точностью е. Используйте указатели на функции как параметры. Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru и Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru .

Литература

1. Керниган, Б. Язык программирования Си / Б. Керниган, Д. Ритчи. – М.: Вильямс, 2007. – 304 с.

2. Подбельский, В.В. Практикум по программированию на языке Си: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2004. – 576 с.

3. Подбельский, В.В. Программирование на языке Си: учеб. пособие / В.В. Подбельский, С.С. Фомин. – М.: Финансы и статистика, 2004. – 600 с.

4. Подбельский, В.В. Язык Си++: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2005. – 560 с.

5. Романов, Е.Л. Практикум по программированию на языке С++: учеб. пособие / Е.Л. Романов. – СПб: БХВ-Петербург, 2004. – 432 с.

6. С/С++. Структурное программирование: практикум / Т.А. Павловская, Ю.А. Щупак. – СПб: Питер, 2004. – 239 с.

Краткая аннотация лекции.

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

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

Текст лекции.

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

Указатель на указатель является формой многочисленного перенаправления или цепочки указателей (рис. 1).

 
  Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru

Рис.1. Представление одиночного и многочисленного перенаправлений

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

Многочисленное перенаправление может и дальше расширяться. Но существует немного случаев, когда необходимо что-то более мощное, чем указатель на указатель. Излишнее перенаправление приводит к концептуальным ошибкам, которые очень трудно исправлять. Переменная, являющаяся указателем на указатель, должна быть описана определенным образом. В С++ это выполняется путем помещения двух звездочек перед именем. Например, следующее объявление сообщает компилятору, что nb – это указатель на указатель типа float:

float **nb;

/*nb – это не указатель на число с плавающей точкой, а указатель на указатель на вещественное число*/

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

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

int *p, x, **q;

x = 10;

p = &x;

q = &p;

printf("%d", **q); /* вывод значения х */

system("pause");

return 0;

}

Здесь р объявляется как указатель на целое, а q – это указатель на указатель на целое. Вызов printf() выводит число 10 на экран.

Так как указатель – это объект в памяти, то можно определять указатель на указатель и т.д. сколько нужно раз. Например, в следующей программе реализовано многочисленное перенаправление и выполнен доступ к значению переменной:

//цепочка указателей на указатели

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

int i=88;

int *pi=&i;

int **ppi=&pi;

int ***pppi=&ppi;

cout << "\n***pppi = " << ***pppi;

system("pause");

return 0;

}

Ассоциативность унарной операции разыменования выполняется справа налево, поэтому последовательно обеспечивается доступ к участку памяти с адресом pppi, затем к участку с адресом (*pppi)== ppi, затем к (*ppi)== pi, затем к (*pi)== i. С помощью скобок последовательность разыменований можно пояснить таким выражением *(*(*pppi))).

Краткие итоги

1.Указатель может реализовать одиночное или многочисленное перенаправление в зависимости от его объявления.

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

3.В языке С++ предусмотрены объявление и вызов функций с переменным числом параметров.

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

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

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

Набор для практики

Вопросы

1. С какой целью в программах используют указатели на указатели?

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

3. Для чего в программе необходима инициализация указателя перед первым его использованием?

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

5. Как в функции с переменным числом параметров осуществляется доступ к списку неизвестных параметров?

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

7. Почему ошибки, связанные с некорректным использованием указателей, относятся к наиболее трудноустранимым?

Упражнения

1.Наберите коды программ из Примеров 1-2. Выполните компиляцию и запуск программ.

2.Напишите функцию, параметрами которой служат адреса трех переменных типа double. Функция должна возвращать адрес (значение указателя) той из переменных, адресуемых параметрами, которая имеет максимальное значение. В основной программе с помощью обращения к функции поменяйте знак значения максимальной из трех переменных. Для решения данной задачи используйте вспомогательную функцию, возвращающую адрес максимальной из переменных, адресуемых двумя параметрами-указателями.

3.Напишите функцию для вычисления значения многочлена Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru Коэффициенты Лекция 4. Производные типы. Тип указатель: указатели на объекты. - student2.ru , аргумент

Наши рекомендации