3_STRUKTURY_DANNYKh_SPP.ppt
- Количество слайдов: 128
СТРУКТУРЫ ДАННЫХ АДРЕСНАЯ АРИФМЕТИКА
План лекции • • • Массивы Строки Структуры Объединения Указатели Взаимосвязь массивов и указателей
СТРУКТУРЫ ДАННЫХ МАССИВЫ
Массивы • Конечная именованная последовательность однотипных величин называется массивом. • В С++ различают массивы фиксированного размера и массивы переменного размера (динамические). • Количество элементов в массиве первого типа известно при написании программы и никогда не меняется. • Память под такой массив выделяет компилятор.
Массивы • Количество элементов динамического массива на этапе компиляции не известно и, как правило, зависит от входных данных. • Память под динамический массив выделяется во время выполнения программы с помощью операций выделения памяти.
Массивы • Описание массива фиксированного размера отличается от описания простой переменной наличием после имени квадратных скобок, в которых задается количество элементов массива (размерность): <Тип элемента> <Имя>[<Размер1>] [<Размер2>]. . . [= {<Список значений >}];
Массивы float a [10]; int a[4][5]; unsigned int koord[10]; float b[10][2]; char buffer[10]; const int array_size = 10; int ia[ array_size ]; int b[100], х[27];
Массивы • Тип элементов массива - любой допустимый тип С++. • Элементами массива не могут быть функции и элементы типа void. • Кроме того, не разрешается использовать массив ссылок. • Элементы массива нумеруются с нуля
Массивы • Глобальный массив по умолчанию инициализируется нулями. • Локальный массив, как и обычная переменная, по умолчанию никак не инициализируется. • Инициализирующие значения при желании задают в фигурных скобках: extern int a[5]={0, -36, 78, 3789, 50};
Массивы const int array_size = 3; int ia[ array_size ] = { 0, 1, 2 }; int b[5] = {3, 2, 1}; //b[0]=3, b[1]=2, b[2]=1, b[3]=0, b[4]=0 // массив размера 3 int ia[] = { 0, 1, 2 }; extern long double c[]={7. 89 L, 6. 98 L, 0. 5 L, 56. 8 L}; // 4 элемента
Массивы • Размерность массива может быть задана только целой положительной константой или константным выражением. • Размерность массивов предпочтительнее задавать с помощью типизированных констант. • Для доступа к элементу массива после его имени указывается номер элемента (индекс) в квадратных скобках.
Массивы #include
Массивы #include
Массивы int count = 0; // Счетчик отрицательных элементов for(i = 0; i < n; i++) if(a[i] < 0) count++; // Подсчет cout << endl; for(i = 0; i < n; i++) cout << a[i] << "t"; // Вывод исходного массива cout << "n. Number of negative elements: t" << count << endl; // Вывод результатов подсчета }
Массивы • В качестве индекса массива может выступать любое выражение, дающее результат целого типа. • Массиву не может быть присвоено значение другого массива, недопустима и инициализация одного массива другим. • Чтобы скопировать один массив в другой, придется проделать это для каждого элемента по отдельности.
Массивы • Многомерные массивы фиксированного размера задаются указанием каждого измерения в квадратных скобках. • В памяти такой массив располагается в последовательных ячейках построчно. • Многомерные массивы размещаются так, что при переходе к следующему элементу быстрее всего изменяется последний индекс.
Массивы • При инициализации многомерного массива для улучшения наглядности элементы инициализации каждого измерения можно заключать в фигурные скобки. • Внутренние фигурные скобки, разбивающие список значений на строки, необязательны и используются, как правило, для удобства чтения кода.
Массивы int mass 2 [][] = { {1, 1}, {0, 2}, {1, 0} }; int mass 2 [3][2] = {1, 1, 0, 2, 1, 0}; int ia[ 4 ][ 3 ] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, { 9, 10, 11 } }; int ia[4][3] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
Массивы • В многомерном массиве размер самого левого измерения также можно не указывать. int MN[][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
Массивы int XYZ[][3][4] = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }, { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } };
Массивы • При обращении к элементам многомерного массива необходимо использовать индексы для каждого измерения (они заключаются в квадратные скобки), например, matr[i][j]. • Конструкция ia[ 1, 2 ] эквивалентна ia[2] !
Массивы int main() { const int row. Size = 4; const int col. Size = 3; int ia[ row. Size ][ col. Size ]; for ( int i = 0; i < row. Size; ++i ) for ( int j = 0; j < col. Size; ++j ) ia[ i ][ j ] = i + j; }
Массивы #include
Массивы void main() { const int N = 4; int a[N][N], i, j, s[N]; for(i=0; i
СТРУКТУРЫ ДАННЫХ СТРОКИ
Строки • Строка представляет собой массив символов, заканчивающийся нульсимволом. • Нуль-символ - это символ с кодом, равным 0, что записывается в виде управляющей последовательности ' '. • По положению нуль-символа определяется фактическая длина строки.
Объявление строки: char <Имя>[<Объем памяти>] [= <Значение>]; char str 1[5] = {‘A’, ’B’, ’C’, ’D’, ’ ’}; Строку можно инициализировать строковым литералом: char str[10] = "Vasia"; char str[] = "Vasia"; В конец символьной строки не требуется обязательно добавлять нуль, компилятор делает это автоматически.
Объявление строки • Обращение к элементам строки осуществляется по индексу, так же как и к элементу массива. Соответственно и нумерация символов начинается с 0. • Операция присваивания одной строки другой не определена и может выполняться с помощью цикла или функций стандартной библиотеки
Строки • Стандартная библиотека предоставляет возможности копирования, сравнения, объединения строк, поиска подстроки, определения длины строки и т. д. • Библиотечные функции работы со строками С объявлены в стандарте устаревшими, так как они не обеспечивают необходимой безопасности.
Строки #include
#include
Массивы строк Char <Имя> [<Количество строк>] [<Мах длина строки>] [= <Значение>]; char str[3][80] = { {'1', '2', '3', '4', '5'}, {'x', 'y', 'z'}, {'A', 'B', 'C', 'D'} }; char str 2[3][80] = { "0123456789", "x_y_z", "A B C D" };
Массивы строк char ms[4][7]={″весна″, ″осень″, ″зима″, ″лето″};
Строки • В С++ поддерживаются два типа строк – встроенный тип, доставшийся от С, и класс string из стандартной библиотеки С++. • Класс string обеспечивает индексацию, присваивание, сравнение, добавление, объединение строк и поиск подстрок, а также преобразование из С-строк, то есть массивов типа char, в string, и наоборот.
СТРУКТУРЫ ДАННЫХ СТРУКТУРЫ ОБЪЕДИНЕНИЯ
Структуры • В отличие от массива, все элементы которого однотипны, структура может содержать элементы разных типов. • В языке С++ структура является видом класса и обладает всеми его свойствами, но во многих случаях достаточно использовать структуры так, как они определены в языке С. struct [ имя_типа ] { тип_1 элемент_1; тип_2 элемент_2; *тип_n элемент_n; } [ список_описателей ];
Структуры • Элементы структуры называются полями структуры и могут иметь любой тип, кроме типа этой же структуры, но могут быть указателями на него. • Если отсутствует имя типа, должен быть указан список описателей переменных, указателей или массивов.
Структуры struct employee { char Name [21]; // имя char Surname [21]; // фамилия int age; // возраст double hourlysalary; // почасовой оклад }; Идентификатор employee является именемэтикеткой (tag name), дескриптором, именует структуру и используется совместно с ключевым словом struct для объявления переменных структурного типа.
Структуры struct employee new_employee, stack[120]; struct employee { char Name [21]; // имя char Surname [21]; // фамилия int age; // возраст double hourlysalary; // почасовой оклад } new_employee, stack[120]; struct { char Name [21]; // имя char Surname [21]; // фамилия int age; // возраст double hourlysalary; // почасовой оклад } new_employee, stack[120];
Структуры Синтаксис инициализации структур аналогичен инициализации массивов. При инициализации структуры ее элементы (инициализаторы) должны соответствовать заданному типу и отделяться друг от друга запятыми. struct employee { char Name [20]; char Surname [20]; int age; double hourlysalary; } new_employee = { "Peter", "Smith", 25, 6. 78 };
Структуры struct complex { float real, im; } compl [2][3]={ {{1, 1}, {1, 1}}, {{2, 2}, {2, 2}} }; Для переменных одного и того же структурного типа определена операция присваивания, при этом происходит поэлементное копирование.
Структуры #include
Структуры Доступ к полям структуры выполняется с помощью операций выбора. (точка) при обращении к полю через имя структуры: имя_переменной_структуры. имя_поля new_employee. Name = «Петров"; new_employee. hourlysalary = 21. 0; stack[4]. hourlysalary = 121. 5;
Структуры Как и в других массивах переменных языка С, в массивах структур индексирование начинается с нуля. Членами структуры могут быть также массивы или структуры. Когда одна структура является членом другой, она называется вложенной : struct X = { int A[7][8]; float b; char ch; struct employee Emp 2; } Y;
Структуры Инициализация элементов двухмерного массива А может быть такой: Y. A[2][5] = 99; Инициализация вложенной структуры может быть следующей: Y. Emp 2. hourlysalary = 12. 75; struct A {int a; double x; }; struct B {A a; double x; } x[2]; x[0]. a. a = 1; x[1]. х = 0. 1;
Объединения Объединение (union) – это тип, который позволяет хранить различные типы данных в одном пространстве памяти (но не одновременно). union <Имя объединения> { <Список элементов объединения>} [<Список переменных [и значений]>]; k[0] k[1] union mem { double d; l long l; d int k[2]; };
Объединения Длина объединения равна наибольшей из длин его полей. В каждый момент времени в переменной типа объединение хранится только одно значение, и ответственность за его правильное использование лежит на программисте. Для доступа к членам объединения используются те же синтаксические конструкции, что и для доступа к членам структуры.
Объединения union tank{ struct { int x, y; int fuel; }; int t[3]; } t 34; t 34. x = 5; t 34. y = 1; t 34. fuel = 20; cout << t 34. t[0] << "n"; cout << t 34. t[1] << "n"; cout << t 34. t[2] << "n"; 5 1 20
УКАЗАТЕЛИ АДРЕСНАЯ АРИФМЕТИКА
Указатели и ссылки • Для хранения адресов в С++ используются специальные типы данных – указатели и ссылки. • Указатель – это переменная, в которой хранится адрес некоторого объекта программы: другой переменной, поименованной константы, подпрограммы и т. п.
Указатели В С++ есть три вида указателей, отличающихся свойствами и набором допустимых операций: – типизированные указатели, – бестиповые указатели, – указатели на функции. [<Изменяемость значения>][<Тип данных>] [<Тип>] [<Изменяемость указателя>] *<Имя>[=<Значение>];
Примеры объявления указателей: int *ip 1, *ip 2; double *dp; void *p; int (*fun) (double, double); Звездочка относится непосредственно к имени, поэтому для того, чтобы объявить несколько указателей, требуется ставить ее перед именем каждого. int *a, b, *c; long *lp, lp 2; float fp, *fp 2;
Типизированные указатели ссылаются на величины определенного типа. Типизированные указатели несут в себе сведения о размере памяти, адресуемой этим указателем. Нетипизированные – создаются как бы «на все случаи жизни» . Они отличаются от типизированных указателей отсутствием сведений о размере соответствующего участка памяти. Указатель на функцию - это особый вид указателя, который применяется для косвенного вызова функции. Указатель функции содержит адрес в сегменте кода, по которому располагается исполняемый код функции.
Указатель может быть константой или переменной, а также указывать на константу или переменную. int i=3, j=4; const int n=5; const int *pi 1; // указатель на константу const int const *pi 11; // указатель на константу int *const pi 2=&j; // константный указатель const int *const pi 3=&n; const // константный указатель на константу
• Обычный указатель pi может менять в процессе работы программы как свое значение, так и значение объектов, на которые он указывает. Его нельзя инициализировать адресом константного объекта, поскольку в противном случае можно было бы модифицировать значение константы косвенно: pi=&i; // верно pi=&n; // ошибка! pi=pi 1; // ошибка! pi=pi 2; // верно
• Указатель на константу pi 1 в процессе работы программы может получать адреса как константных, так и неконстантных объектов. Однако, изменять их значения указатель на константу не может: pi 1=&n; cout<<*pi 1; // верно pi 1=&j; *pi 1=7; // ошибка!
• Константный указатель pi 2, напротив, должен (как и любая константа) инициализироваться при объявлении. Менять свое значение в процессе работы он не может: *pi 2=7; // верно pi 2=&j; // ошибка! • Константный указатель на константу pi 3 не может модифицировать ни свое значение, ни значение указываемого им объекта.
Инициализация указателей Присваивание указателю адреса существующего объекта: • с помощью операции получения адреса: int a = 5; // целая переменная int *p = &a; //в указатель записывается адрес a int *p (&a); // то же самое другим способом • значения другого инициализированного указателя: int *r = p;
Инициализация указателей • имени массива или функции, которые трактуются как адрес: int b[10]; // массив int *t = b; // присваивание имени массива . . . void f(int a ){ /* … */ } // определение функции void (*pf)(int); // указатель на функцию pf = f; // присваивание имени функции
Инициализация указателей Присваивание пустого значения: int *suxx = NULL; // не рекомендуется int *rulez = 0; // так - лучше Указатели, инициализированные значением называются нулевыми и ни на что не указывают. 0, Выделение участка динамической памяти и присваивание ее адреса указателю: int *n = new int; int *m = new int (10); int *q = new int [10]; int *u = (int*)malloc(sizeof(int));
Операции с указателями С указателями можно выполнять следующие операции: • разыменование (*), • присваивание, • сложение с константой, • вычитание, • инкремент (++), декремент (- -), • сравнение, • приведение типов, • операция получения адреса (&).
Операция разыменования • Операция разыменования или косвенная адресация, предназначена для доступа к величине, адрес которой хранится в указателе. • Эту операцию можно использовать как для получения, так и для изменения значения величины (если она не объявлена как константа): char a; char *p = new char; *p = 'Ю'; a = *p;
Операция разыменования short c, a=5, *ptri=&a; float d, p=2. 4563; void *b=&a; … c=*ptri; // в результате с=5 *ptri=125; // вместо 5 в память переменной а // теперь записано число 125
Операция разыменования int x = 1, y = 2; int *ptr; //объявили указатель на целую переменную ptr = &x; // взяли адрес переменной х = 2 y = *ptr; // переменная y стала равной 1 *ptr = 0; // переменная х стала равной 0
Операция разыменования • Нетипизированные указатели разыменовывать нельзя • При необходимости разыменовать нетипизированный указатель требуется явно указать тип данных, например: int c, a=5, *ptri=&a; void *b=&a; *b=6; *(int*)b=6; Явное переопределение типа указателя
Операция разыменования • Одной из распространенных ошибок является разыменование неинициализированных указателей. Например, в любом из следующих случаев результат работы программы непредсказуем: char* s; *s= ’a’; // ошибка! cin >> s; // ошибка!
Присваивание указателей • Присваивание без явного приведения типов допускается только – указателям типа void* – если тип указателей справа и слева от операции присваивания один и тот же. • Присваивание типизированных указателей указателям функций (и наоборот) недопустимо.
Присваивание указателей int a, *ptri, *ptrj; void *b; ptri=&a; *ptri=0; ptri=ptrj; b=&a; ptri=b; ptri=(int *) b; Явное переопределение типа указателя
Присваивание указателей int *p 1, *p 2; float *p 3, *p 4; void *p; // допустимые операции p 1=p 2; p 4=p 3; p 1=0; p=NULL; // недопустимые операции p 3=p 2; p 1=p 3; // явное переопределение типа p 3=(float*)p 2; p 1=(int*)p 3;
Присваивание указателей #include
Получение адреса (&) • Результат операции – адрес некоторой области памяти, который можно присвоить указателю. Это можно сделать: – при помощи операции присваивания: int *pi, i=10; pi=&i; – во время инициализации указателя при его объявлении: float b=5. 7, *pf=&b; • Нельзя получить адрес скалярного выражения, неименованной константы или регистровой переменной.
Арифметические операции • Сложение с целым, вычитание, инкремент и декремент автоматически учитывают размер типа величин, адресуемых указателями. • Эти операции применимы только к типизированным указателям одного типа и имеют смысл в основном при работе со структурами данных, последовательно размещенными в памяти, например, с массивами.
Адресная арифметика • Совокупность указанных операций и правила выполнения этих операций над указателями получили название адресной арифметики. • Основное правило адресной арифметики: при арифметики увеличении или уменьшении адреса, хранящегося в указателе, на количество единиц n значение адреса изменяется на n, умноженное на размер элемента данных, единиц: <Указатель> + n <Адрес> + n*sizeof(<Тип данных>)
Адресная арифметика short a, *ptrs =&a; ptrs++; Значение указателя меняется ptrs+=4; Значение указателя меняется *(ptrs+2)=2; Значение указателя не меняется!!!
Адресная арифметика • Разность двух указателей - это разность их значений, деленная на размер типа в байтах. • Из равенства p+n==p 1 следует, что p 1 -p==n. • Значением оператора разности двух указателей является целое, равное количеству объектов рассматриваемого типа между адресами от p до p 1. • Это – единственный случай в языке, когда результат бинарного оператора с операндами одного типа принадлежит к принципиально другому типу.
Адресная арифметика • Инкремент перемещает указатель к следующему объекту рассматриваемого типа , декремент - к предыдущему. • Два указателя можно сравнивать между собой, используя операторы отношения >, <, >=, <=, ==, !=. if(p < q) printf("p ссылается на меньший адрес, чем q");
Адресная арифметика int main (void) { int x, y; int *px, *py; ptrdiff_t z; // Взятие адресов переменных px = &x; py = &y; // Разница двух указателей z = px - py; printf("n The difference of two pointers to %p and %p is: %d", px, py, (int) z); printf("nn The addresses are: px = %p, py = %pn", &px, &py); printf("n Press any key: "); _getch(); return 0; }
Ссылки • Кроме указателя в С++ для хранения адреса может использоваться ссылка. • Ссылка определена как альтернативное имя уже существующего объекта. • Основные достоинства ссылок проявляются при работе с возвращаемыми параметрами функций. • Правила описания ссылок: <Тип данных> &<Имя>[= <Выражение>] или <Тип данных> &<Имя>[(<Выражение>)]
Ссылки int L=127; int &SL=L; // первая форма int &SL(L); // вторая форма int kol; int &pal = kol; const char &CR = 'n'; int a, // переменная *ptri=&a, // указатель &b=a; // ссылка … a=3; *ptri=3; b=3
Ссылки • Ссылка - это синоним имени объекта, или псевдоним, или ярлык. • Сама ссылка объектом не является, поэтому память для нее не выделяется. • Поскольку ссылка является другим именем объекта, все операции со ссылкой выполняются на самом деле с самим объектом. • Ссылки подчиняются общим правилам определения области видимости, действия и времени жизни.
Ссылки • Указатель содержит адрес и при работе с адресуемыми им данными необходимо использовать операцию разыменования. • Ссылка объявляется как альтернативное имя, поэтому при работе с данными по ссылке разыменование не нужно. • Переменная-ссылка должна явно инициализироваться при ее описании, кроме случаев, когда она является параметром функции, описана как extern или ссылается на поле данных класса.
Ссылки • После инициализации ссылке не может быть присвоена другая переменная. • Тип ссылки должен совпадать с типом величины, на которую она ссылается. • Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.
Управление динамической памятью • Динамически распределяемая область памяти – это свободная область памяти, не используемая программой, операционной системой или другими программами (англ. heap – «куча» ). • С++ включает набора средств, позволяющих организовать работу с динамически выделяемой памятью: унаследованный от С и реализованный в С++. • В программе не следует смешивать использование средств разных языков.
Управление динамической памятью • Основу системы динамического распределения памяти в С составляют библиотечные функции calloc(), malloc(), realloc() и free(). • Для работы с динамической памятью в С++ используют специальные операторы new и delete • Перед попыткой использовать распределенную память важно проверить, что возвращаемое значение не равно нулю.
Функция calloc() void *calloc(size_t num, size_t size); size – параметр, определяющий размер выделяемой области; num– количество областей. num • Функция calloc() выделяет память, размер которой равен значению выражения num * size. • Выделенная область памяти обнуляется. • Функция calloc() возвращает указатель на первый байт выделенной области или NULL, если запрос на память выполнить нельзя.
Функция calloc() • Тип void может быть переопределен для требуемого типа, т. е. для char, int, float, double *ptr; ptr = (double *) (calloc(10, sizeof(double))); if (!ptr) // условие логического отрицания {printf("Out of memory. Press any key: n"); _getch(); exit(1); }
Функция calloc() int *list; list = (int *) calloc(3, sizeof(int)); *list=-244; *(list+1)=15; *(list+2)=-45; … free(list);
Функция malloc() void *malloc (size_t size); size – параметр, определяющий размер выделяемой области. • Функция malloc() возвращает указатель на первый байт области памяти размера size или NULL, если запрос на память выполнить нельзя. • Выделенная область памяти не инициализируется.
Функция malloc() int *a; if ((a=(int *)malloc(sizeof(int)))==NULL) { printf("Память для числа не выделена. "); exit(1); } *a=-244; *a+=10; free(a);
Функция realloc() void *realloc(void *ptr, size_t size); *ptr - указатель, size –размер выделяемой области. • Функция realloc() изменяет размер блока ранее выделенной памяти. Значение параметра size может быть больше или меньше, чем перераспределяемая область. • Возвращает указатель на блок памяти, т. к. бывает необходимо переместить этот блок. • Если новый размер памяти больше старого, дополнительное пространство не инициализируется.
Функция realloc() int input, n; int count=0; int *numbers = NULL; int *more_numbers; do { printf ("Enter an integer value (0 to end): "); scanf ("%d", &input); count++; more_numbers = (int*) realloc(numbers, count * sizeof(int));
Функция realloc() if (more_numbers!=NULL) { numbers=more_numbers; numbers[count-1]=input; } else { free (numbers); puts ("Error (re)allocating memory"); exit (1); } } while (input!=0); printf ("Numbers entered: "); for (n=0; n
Функция free() void free(void *ptr); ptr – параметр, определяющий адрес освобождаемого блока памяти. • Функция free() возвращает в динамически распределяемую область памяти блок, после чего эта память становится доступной для выделения в будущем. • Вызов функции free() должен выполняться только с указателем, который был получен ранее в результате вызова одной из функций динамического распределения памяти.
Функция free() #include
Операторы new и delete • Выделение памяти под одно значение: <Типизированный указатель> = new <Тип>[(<Значение>)]; <Тип> – тип значения, под размещение которого выделяется память – определяет размер памяти, выделяемый данным оператором; <Значение> – инициализирующее значение. • Освобождение памяти, выделенной под одно значение: delete <Типизированный указатель>;
Операторы new и delete • Выделение памяти под несколько значений: <Указатель> =new<Тип>[<Количество>]; <Тип> – тип значения, под размещение которого выделяется память – определяет размер памяти, выделяемый данным оператором; <Количество> – количество значений, которое необходимо разместить в памяти.
Операторы new и delete • Освобождение памяти, выделенной под несколько значений. delete[]<Типизированный указатель>; Квадратные скобки присутствуют в операторе и указывают, что освобождается несколько значений, память под которые отводилась одним оператором new:
Операторы new и delete int *k; k = new int; new *k = 85; delete k; delete int *a; if ((a = new int(-244))==NULL){ new printf("Память для числа не выделена. "); exit(1); } delete a; delete
Операторы new и delete short *list; list = new short[3]; *list=-244; *(list+1)=15; *(list+2)=-45; delete[] list;
Операторы new и delete int i, n; int *p; cout << "How many numbers would you like to type? "; cin >> i; p = new int[i]; new if (p == 0) cout << "Error: memory could not be allocated"; else { for (n=0; n> p[n]; } cout << "You have entered: "; for (n=0; n
ВЗАИМОСВЯЗЬ МАССИВОВ И УКАЗАТЕЛЕЙ
Указатели и одномерные массивы • В языке С++ между указателями и массивами существует тесная связь. • Когда объявляется массив, происходит выделение памяти для элементов массива и для указателя с именем массива • В языке С++ имя массива без индексов трактуется как адрес начального элемента. То есть имя массива является указателем на массив. • Таким образом, доступ к элементам массива осуществляется через указатель с именем массива.
Указатели и одномерные массивы Поскольку имя массива является указателем, допустимо, например, такое присваивание: int arrаy[25]; int *ptr; ptr=array; //эквивалентно ptr=&arrаy[0] Здесь указатель ptr устанавливается на адрес первого элемента массива.
Указатели и одномерные массивы • Адрес каждого элемента массива можно получить, используя одно из трех выражений: Индекс 0 Адрес array i - го &array[0] элемента ptr 1 2 array +1 &array[1] ptr + 1 array +2 &array[2] ptr + 2 3 4 array +3 array +4 &array[3] &array[4] ptr + 3 ptr + 4 • А обращение к пятому элементу массива можно записать как: array[4], *( array + 4), *(рtr + 4)
Указатели и одномерные массивы • С++ дает два способа обращения к элементу массива: – с помощью индексированного имени; – посредством арифметики с указателями. • Версия с индексированным именем выполняется медленнее, так как в языке С++ на индексирование массива уходит больше времени, чем на выполнение оператора *. • Использование указателей для доступа к элементам массива в языке С++ применяется довольно часто.
Указатели и одномерные массивы int array[10]; int *p; p=&array[0]; *array=2; printf("%dn", array[0]); array[0]=2; printf("%dn", array[0]); *(array+0)=2; printf("%dn", array[0]); *p=2; printf("%dn", array[0]); p[0]=2; printf("%dn", array[0]); *(p+0)=2; printf("%dn", array[0]);
Указатели и двумерные массивы • Элементы двумерных массивов располагаются в памяти подряд по строкам. • Такой порядок дает возможность обращаться к любому элементу двумерного массива, используя адрес его начального элемента и индексное выражение. int arr[m][n]: Адрес (arr[i][j])= Адрес(arr[0][0]) + (i*n+j)*k, где k – количество байтов, выделяемое для элемента массива (в зависимости от типа).
Указатели и двумерные массивы • Указатели на двумерные массивы в языке С++ – это массивы массивов, т. е. такие массивы, элементами которых являются массивы. • Объявление arr[4][3] порождает в программе три разных объекта: – указатель с идентификатором arr, – безымянный массив из четырех указателей: arr[0], arr[1], arr[2], arr[3], – безымянный массив из двенадцати чисел типа int.
Указатели и двумерные массивы 1) В памяти выделяется участок для хранения значения переменной arr, которая является указателем на массив из четырех указателей. 2) Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес одномерного массива из трех элементов типа int. 3) Следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов.
Указатели и двумерные массивы Схематично распределение памяти для данного двумерного массива выглядит так: arr[0][0] arr[0][1] arr[0][2] arr[0] arr[1][0] arr[1][1] arr[1][2] arr[1] arr[2][0] arr[2][1] arr[2][2] arr[3][0] arr[3][1] arr[3][2] arr[3]
Указатели и двумерные массивы • Для доступа к безымянным массивам используются адресные выражения с указателем arr. • Доступ к элементам одномерного массива указателей осуществляется с указанием одного индексного выражения в форме arr[2] или *(arr+2). • Для доступа к элементам двумерного массива чисел типа int arr[i][j] могут быть использованы разные выражения.
Указатели и двумерные массивы обращение к элементу arr[1][2]: arr[i][j] arr[1][2]=10 *(*(arr+i)+j) *(*(arr+1)+2)=10 (*(arr+i))[j] (*(arr+1))[2]=10 с помощью указателя ptr: int *ptr=arr; обращение к элементу arr[1][2]: ptr[1*3 + 2] /*1 и 2 - это индексы элемента, а 3 - это число элементов в строке*/ ptr[5] *(*(ptr + 1) + 2) *(ptr + 1*3 + 2)
Указатели и двумерные массивы int i, j; int t[2][3]; for(i=0; i<2; i++) for(j=0; j<3; j++) t[i][j]=i+j; for(i=0; i<2; i++) for(j=0; j<3; j++) printf(" %d ", *(*(t + i) +j) ); //или printf(" %d ", (*(t + i))[j]);
Указатели и двумерные массивы int i, j; int t[2][3], *ptr; ptr=&t[0][0]; for(i=0; i<2; i++) for(j=0; j<3; j++) t[i][j]=i+j; for(i=0; i<2; i++) for(j=0; j<3; j++) printf(" %d ", *(ptr+i*3+j)); printf("nn");
Указатели и двумерные массивы //С матрицей так делать нельзя /* for(i=0; i<2; i++) for(j=0; j<3; j++) printf(“%d", *(t + i*3 +j)); */ //Корректная работа с матрицей for(i=0; i<6; i++) printf(" %d ", ptr[i] ); printf("nn"); for(i=0; i<2; i++) for(j=0; j<3; j++) printf(" %d ", ptr[i*3 +j] );
Указатели на структуры • Указатель на структуру объявляется точно так же, как и указатель на данные простых типов: используется операция '*' и указывается тип данных. Тип данных структуры указывается заданием ключевого слова struct и имени шаблона этой структуры. Имя. Структурного. Типа *Имя. Указателя. На. Структуру Например: struct goods *p_goods; struct student *p_stu 1, *p_stu 2;
Указатели на структуры могут вводиться и для безымянных (не имеющих имен) структурных типов. Например: struct{ char *name; int age; } *person; //указатель на структуру
Указатели на структуры При определении указателя на структуру он может быть инициализирован. Корректно в качестве инициализирующего значения применять адрес структурного объекта того же типа, что и тип определяемого указателя. Значение указателя на структуру может быть определено и с помощью присваивания.
Указатели на структуры struct particle { double mass; float coord[3]; } dot[3], point, *pinega; //Инициализация указателей struct particle *p_d=&dot[1], *pinta=&point; pinega=&dot[0];
Указатели на структуры При определении элемента структуры запрещено указание в качестве элемента самого себя (через структурный объект). struct STUD { //некорректное объявление поля структуры STUD t; } a, b; Однако элемент структуры может указателем на определяемую структуру. struct STUD { STUD *pt; } a, b; быть
Указатели на структуры Указатель на структуру обеспечивает доступ к ее элементам двумя способами: (*Указатель. На. Структуру). Имя. Элемента или Указатель. На. Структуру->Имя. Элемента В первом случае круглые скобки необходимы, чтобы учесть приоритет операций. Во втором случае используется операция «стрелка» (->), которая называется операцией косвенного выбора элемента структурного объекта, адресуемого указателем.
Указатели на структуры struct student { char name[10]; char surname[15]; int age; } stud; struct student *s; s=&stud; // инициализация указателя s->name="Larry“; s->surname="Johnson“; (*s). age=34;
Указатели на структуры struct book { char title[40]; char authors[30]; char publishing_house[15]; int year; int pages; } *b ;
> b->title; cout << "Автор: ";" src="https://present5.com/presentation/169000963_322403927/image-127.jpg" alt="Указатели на структуры cout << "Название: "; cin >> b->title; cout << "Автор: ";" />
Указатели на структуры cout << "Название: "; cin >> b->title; cout << "Автор: "; cin >> b->authors; cout << "Издательство: "; cin >> b->publishing_house; cout << "Год: "; cin >> b->year; cout << "Количество страниц: "; cin >> b->pages;
• Дьюхерст, С. C++. Cвященные знания – М. : Символ-плюс, 2007. – 240 с. • Мейерс, С. Эффективное использование C++, 55 верных советов улучшить структуру и код ваших программ – М. : ДМК пресс, 2006. – 300 с. • Уилсон, М. C++. Практический подход к решению проблем программирования – М. : КУДИЦ-Образ, 2006. – 736 с. • Джосьютис, Н. C++. Стандартная библиотека – СПб. : Питер, 2004. – 736 с.


