111.50K
Категория: ПрограммированиеПрограммирование

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

1.

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

2.

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

3.

Указатели
Существуют следующие способы
инициализации указателей:
1. Присваивание указателю адреса
существующего объекта:
- с помощью операции получения адреса &
int var_int = 6338;
int *ptr_int = &var_int;
// или
int *ptr_int(&var_int);

4.

Указатели
Еще один пример:
struct Test
{
int test_int;
char test_char;
double tetst_double;
} test={10, 'A', 4.78};

5.

Указатели
int main()
{
Test *ptr_Test = &test;
cout << ptr_Test -> test_int << ' '
<< ptr_Test -> test_char << ' '
<< ptr_Test -> tetst_double << endl;
system("pause");
return 0;
}

6.

Указатели
- с помощью значения другого
инициализированного указателя
float var_float = 3.55f;
float *ptr_float_1 = &var_float;
float *ptr_float_2 = ptr_float_1;

7.

Указатели
- с помощью имени массива
unsigned array_int[] = {33, 51, 78, 4, 15};
int main()
{
unsigned *ptr_array = &array_int;
обязательно
cout << *(ptr_array + 3) << endl;
system("pause");
return 0;
}
// & - не

8.

Указатели
Известно, что имя массива является
указателем на его первый элемент, поэтому
следующие выражения эквивалентны:
cout << *ptr_array << endl;
cout << *array_int << endl;
В обоих случаях получим значение первого
элемента массива.

9.

Указатели
- с помощью имени функции
void func(int,char){ // ……};
void (*ptr_fun)(int,char) = &func;
Знак операции взятия адреса & здесь также
не обязателен, так как имя функции
трактуется как адрес ячейки памяти,
начиная с которого находится объектный
код данной функции.

10.

Указатели
2. Присваивание указателю адреса области
памяти в явном виде:
char *vp = (char *)0xB8000000;
int main()
{
cout << *vp << endl;
system("pause");
return 0;
}
// ???????

11.

Указатели
3. Присваивание пустого указателя:
short *ptr_short = NULL;
long *ptr_long = 0;
4. Выделение участка динамической памяти:
- с помощью операции new:
int *p_i = new int;

12.

Указатели
Здесь в динамической памяти выделяется
место для хранения величины типа int.
int *p_j = new int(100);
Здесь помимо выделение памяти, заносится
значение 100.
int *p_array new int[10];
Здесь выделяется место в динамической
области памяти для хранения массива
целых чисел.

13.

Указатели
- с помощью функции malloc:
int *p_i = (int *)malloc(sizeof(int));
Функция malloc заимствована из языка С, тем не
менее, она работает.
При работе с динамической областью памяти
необходимо следить за тем, чтобы указатель
не вышел за пределы области видимости. В
этом случае память отведенная под указатель
освобождается, указатель обнуляется, а
переменная становится недоступной.

14.

Указатели
На программистском сленге это означает
появление «мусора» в памяти.
Полезный совет при попытке выделения памяти
в динамической области. Проверяйте
значение указателя после выполнения
операции new или функции malloc на
равенство нулю. Если указатель нулевой, то
операционной системе не удалось найти
достаточного свободного объема. Это
предотвратит последующие ошибки.

15.

Указатели
int main()
{
float *ptr_float = new float(67.44f);
if(!ptr_float)
{
cout << " Недостаточно памяти " << endl;
exit(1);
}
else cout << " Ok " << endl;
system("pause");
return 0;
}

16.

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

17.

Указатели
Определим основные операции допустимые над
указателями:
- операция разадресации (разыменования),
косвенное обращение к объекту (*);
- присваивание;
- сложение с константой;
- вычитание константы;
- инкремент (--);
- декремент (++);

18.

Указатели
- сравнение;
- приведение типов.
При работе с указателями очень часто
используется операция получения (взятия)
адреса (&).
Основной операцией над указателями является
операция разадресации или разыменования,
которая предназначена для доступа к
величине, на которую указывает указатель.

19.

Указатели
Эта операция симметрична, то есть с ее помощью можно получить
значение объекта или изменить его.
Рассмотрим пример:
int main()
{
typedef unsigned long int UINT;
//
UINT *ptr_UINT = new UINT(12L);
// объявление и инициализация указателя на объект UINT
cout << " Величина UINT " << *ptr_UINT << endl;
// получение значения по указателю
return 0;
}

20.

Указатели
Эта операция применима только к указателям
на объект какого-либо типа и на тип void. К
указателям на функцию она не имеет
смысла.
Арифметические операции над указателями,
в частности, сложение с константой,
вычитание константы, инкремент,
декремент допустимы, но не над всеми
видами указателей.

21.

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

22.

Указатели
Рассмотрим пример использования арифметических
операций:
float array_float = {3.2, -44.6, -0.073, 12, 5.0};
// ….
float summa_array(float arr[])
{
float rez = 0;
for(int i =0; i<5; i++)
rez += *(arr+i); //сложение указателя с константой
return rez;
}

23.

Указатели
Операции инкременты, декремента, вычитание
константы рассмотреть самостоятельно.
Важную операцию представляет операция
преобразования указателей. Преобразование
указателей в С++ допускается двумя
способами:
- унаследованным от языка С. Общий формат
оператора преобразования следующий:
(тип *) имя_указателя;, или
тип * (имя_указателя);.

24.

Указатели
- в стиле языка С++, используя операции
static_cast, dynamic_cast, reinterpret_cast,
например,
static_cast<float *> (ptr_int);
Операции преобразования широко
используются при преобразовании в
иерархии родственных классов в условиях
наследования.

25.

Указатели
Обычно операция преобразования используется
при выполнении операции присваивания.
Присваивание без явного преобразования
допускается если в левой части выражения
используется указатель на тип void или типы
указателей совпадают. Значение 0 неявно
приводится к указателю на любой тип.
Присваивание указателей на объекты
указателям на функции и наоборот не
допускается.

26.

Ссылки
Ссылки
Ссылка (ссылочный тип данных) – синоним
имени, указанного при инициализации
ссылки. Ссылку можно рассматривать как
указатель, который не надо
разыменовывать. Формат объявления
ссылки следующий:
тип_ссылки &имя_ссылки = инициализация;

27.

Ссылки
Пример объявления ссылки:
int var_int = 57;
int &ref_int = var_int;
const char &ref_char = ‘\n’;
Следует запомнить следующие правила
определения и использования ссылок:
- переменная-ссылка должна явно
инициализироваться при ее описании, кроме
случаев, когда она является параметром функции,
описана как extern или ссылается на поле данных
класса;

28.

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

29.

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

30.

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

31.

Массивы
Общий формат описания массива:
тип_массива имя_массива[размерность] =
{инициализация};
Массивы в С++ имеют ряд особенностей:
- нумерация (индексация) элементов
начинается с нуля;
- компилятор не отслеживает границ
массива.

32.

Массивы
Примеры объявлений массивов:
double array_double[20];
// объявление массива из 20 чисел типа double
int array_int[10] = {34, 86, -53, 1024, 0, -778};
// объявление массива из 10 целых чисел с
инициализацией
int array_int[] = {553, 749, -503, 46, 120, 59};
// тоже, но без указания размерности

33.

Массивы
Рассмотрим пример:
int array_int[10] = {32, -453, 6, 562, -322, 78};
int main()
{
for(int i=0; i<=5; i++)
cout << " Array: " << array_int[i] << ' ';
cout << endl;
return 0;
}

34.

Массивы
Здесь объявлен массив целых чисел
array_int[10] и инициализирован
значениями. Инициализация массива
осуществлена не полностью, только первые
6 элементов. Все остальные заполняются
нулями целого типа. Если при объявлении
не указана размерность, инициализация
массива обязательна.

35.

Массивы
Обращение к элементам массива можно
осуществлять двумя способами:
- с помощью операции индексирования – [n],
как в приведенном выше примере;
- через указатель.
Как уже было сказано, имя массива
компилятором понимается как указатель на
его первый элемент.

36.

Массивы
Поэтому выражение
cout << " Array: " << array_int[i] << ' ';
можно записать в следующем виде:
cout << " Array: " << *(array_int+i) << ' ';
В памяти машины все элементы массива
будут расположены в последовательных
ячейках ОЗУ.

37.

Массивы
array_int *
32
-453
6
562
-322
78
0
0
0
0
Размерность массива принято задавать с
помощью именованных констант, например:
const int n_str=10, n_stb=15;,
поскольку при таком подходе значение
константы достаточно скорректировать только
в одном месте.

38.

Массивы
Многомерные массивы
Многомерные массивы задаются указанием
каждого измерения в отдельных
квадратных скобках, например,
int matr[6][8];.
Здесь задается двумерный массив целых
чисел, состоящий из 6 строк и 8 столбцов.

39.

Массивы
Инициализация многомерного массива также
допускается, например,
int arr_int[3][3] = {{1,2,3}, {2,3,4}, {3,4,5}}; или
же:
int arr_int[3][3] = {1,2,3,2,3,4,3,4,5};
Для доступа к многомерному массиву можно
использовать операцию индексирования arr_int[2,1] или посредством указателя *(*(arr_int+2)+1).

40.

Массивы
Многомерные массивы размещаются в
памяти так, что при переходе к следующему
элементу, быстрее всех изменяется
последний индекс.
Массивы можно объявлять в динамической
области памяти, например,
const int nstr =5, nstb =6;

41.

Массивы
int **array_int = new int *[nstr];
for(int i=0; i<nstr; i++)
array_int[i] = new int[nstb];

42.

Массивы
Строки
Строка – это массив символов,
заканчивающийся нуль-символом (‘\0’).
По положению нуль-символа компилятор
определяет конец строки. В отличие от
обычного массива строка занимает на один
элемент больше (под нуль-символ).

43.

Массивы
Рассмотрим простой пример:
char str[10] = "Hello!";
int main()
{
int i=0;
while(str[i] != '\0')
{
cout << str[i];
i++;
}
cout << endl;
return 0;
}

44.

Массивы
Строку можно задать как указатель на
константную величину:
char *str = "Hello!";
Изменение элементов этой строки не
допускается.
Операции над строками можно осуществлять
через операторы цикла, кроме того, много
операций определено в стандартной
библиотеке.
Стандартная библиотека размещена в файле
<string>.

45.

Массивы
До сих пор мы говорили о массивах, содержащих
объекты стандартные типы. А можно ли
создавать массивы объектов
пользовательского типа?
Рассмотрим пример простой структуры:
struct Student
{
string Name;
int Age;
};

46.

Массивы
Объявим массив типа Student:
Student arr_Student[10];
Воспользуемся этим массивом
arr_Student[0].Name = "Иван";
arr_Student[0].Age = 20;
arr_Student[1].Name = "Маша";
arr_Student[1].Age = 19;

47.

Массивы
Заметим, что отличия в обращении к
элементам такого массива есть, в частности,
используется операция доступа к полям
структуры (‘ . ’).
Еще один вариант обращения – через
указатель (имя массива – указатель на его
первый элемент):
(arr_Student+2)->Name = "Вася";
(arr_Student+2)->Age = 19;

48.

Массивы
И здесь есть отличие – использование
операции доступа ‘ -> ’ производит
автоматическое разыменование указателя
и поэтому символ звездочки перед
указателем не ставится.

49.

Массивы
Следующий пример связан с объявлением
массива указателей на функции.
Предположим, что есть ряд одинаковых
функций, выполняющих разные действия:
int add(int a, int b)
{
return a+b;
}

50.

Массивы
int sub(int a, int b)
{
return a-b;
}
int mul(int a, int b)
{
return a*b;
}

51.

Массивы
Объявим массив указателей на функции:
typedef int (*PF)(int,int);
PF ptr_fun[5] = {&add, &sub, &mul,0,0};
Теперь можно вызывать функции, обращаясь к
элементам массива:
int v_int_1 = 10, v_int_2 = 5;
cout << (ptr_fun)[0](v_int_1, v_int_2) << endl;
cout << (ptr_fun)[1](v_int_1, v_int_2) << endl;
cout << (ptr_fun)[2](v_int_1, v_int_2) << endl;

52.

Массивы
Результат посмотреть обязательно.
Следующий вариант вызова функции в работу
– через указатель:
cout << (*(ptr_fun+1))(v_int_1, v_int_2) << endl;

53.

Массивы

54.

Массивы

55.

Массивы

56.

Массивы

57.

Массивы

58.

Массивы
English     Русский Правила