Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Технологии разработки объектно-ориентированных программ на язык C++. Основы структурного программирования на алгоритмическом языке C++

.pdf
Скачиваний:
4
Добавлен:
12.11.2023
Размер:
3.17 Mб
Скачать

Рис. 8.6. Вкладка «Видимые переменные»

Рис. 8.7. Вкладка «Контрольные значения»

51

Вкладка «Контрольные значения» (рис. 8.7) позволяет вводить конкретные переменные и отслеживать их значения на протяжении всей программы. Изначально это окно пустое. Это полезно, если необходимо следить за изменениями одной конкретной переменной. Чтобы добавить новую переменную, нужно нажать мышкой на первую строку и ввести имя необходимой переменной.

52

Глава 9. МАССИВЫ И УКАЗАТЕЛИ

9.1. Статические массивы

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

int mas [10]; // Описание массива из 10 целых чисел

Размерность – количество элементов массива. Если не указать размерность, но указать все элементы массива, то память зарезервируется по количеству элементов.

Вопреки человеческой привычке нумерация элементов в массиве начинается с нуля. Если в массиве хранится десять чисел, последняя позиция будет иметь номер 9.

Также важно, что объявление массива требует выделить память под десять переменных целого типа. Когда программа покидает блок, в котором находится объявление массива, память автоматически очистится.

Заполнение массива

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

Внимание! При обращении к элементам массива автоматический контроль выхода индекса за границу массива не производится, что может привести к ошибкам [10].

53

mas [0] = 7;

cout << mas[0] ; // Выведет на экран 7.

Этот пример демонстрирует, как первому элементу массива присваивается определенное (не случайно выбранное) число. Подобным образом можно заполнить весь массив одним числом:

for (i=0; i=9; i++) { mas [i] = 7;

}

весь массив разными числами в определенном порядке (или введенными с клавиатуры числами):

for (i=0; i=9; i++) { cin >> mas [i];

}

весь массив числами, полученными с помощью генератора случайных чисел (это называется рандомизацией):

for (i=0; i=9; i++) {

mas [i] = rand () % 100 + 1;

}

В последнем примере массив заполняется числами, полученными рандомизацией, от 1 до 100. Если бы мы не прибавили 1, то от

0 до 99 [10].

Пример поиска максимального элемента:

Задача: разработать и составить программу для поиска максимального элемента массива:

#include <iostream> using namespace std; int main() {

setlocale (LC_ALL, "Russian");

//Эта команда подключает русский язык в консоли int const size = 10;

int mas[size] = { 8,4,5,3,7,1,0,4,10,3 };

//Пример задания заранее известных элементов массива

54

int max = mas[0]; int nom = 0;

//Будем выводить порядковый номер. Нумерация с 0! for(int i = 0; i < size; i++) {

//Объявили переменную счетчика в условии цикла сout << mas[i] << ", ";

if (max < mas[i]) {

max = mas[i]; // Ищем максимальный элемент

nom = i; }

}

cout << endl << "Максимальный элемент равен = " << max << endl <<

"Его номер = " << nom << endl; system ("PAUSE");

return 0;

}

/*

Для поиска минимального элемента нужно изменить знак в условии цикла

и в ветвлении на >

*/

Кроме того, размерность массива можно найти с помощью функции sizeof:

n = sizeof mas/sizeof int;

Необходимо посчитать, сколько байт занимает наш массив, а потом разделить на количество байт одного элемента типа int. Получим размерность (рис. 9.1).

Рис. 9.1. Результат работы программы по поиску максимального элемента

9.2. Указатели

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

55

оператор «*». Если р – это указатель на переменную типа int, то *р – это сама переменная.

int *p; // р – указатель на int.

После объявления указатель еще не связан с объектом и указывает «пальцем в небо». Для того чтобы привести указатель в рабочее состояние, необходимо присвоить ему адрес реального объекта. Делается это оператором & (есть различные способы, но о них чуть позже). Это называется разадресацией – мы берем у объекта его адрес:

#include <iostream>

 

using namespaсe std;

 

int main () {

 

int a = 2;

 

int *p;

// р – указатель на int.

p = &a;

// Указателю присвоили адрес

объекта.

 

cout << “*p = “ << *p <<endl;

// *p=2.

*p = 5;

// a = 5;

}

 

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

9.3. Виды указателей

Указатель на функцию

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

Указатель функции должен иметь тип самой функции, а тип аргументов должен соответствовать аргументам функции:

тип (*имя) (список_типов_аргументов);

56

Например, объявление

int (*fun) (double, double);

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

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

#include <iostream> #include <math.h>

//Эта директива подключает математические операции using namespace std;

//Функция для вычисления площади круга радиусом R double raschet (double R) {

const double PI = 3.1415; // Объявляем константу return PI * R * R; // Возвращаем посчитанное

значение

}

int main() {

setlocale(LC_ALL, "rus"); // Подключение русского

double r = 2.0;

 

// Выбираем радиус

cout << "для круга радиусом " << r << " площадь = "

<< raschet (r)

 

 

<< endl; // Демонстрируем обычный вызов функции

double (*p) (double);

// Создаем переменную – ука-

затель на функцию

 

 

p = raschet;

/* Адрес заносим в указатель.

 

 

Внимание! & –

 

 

не нужен! */

cout << "для круга радиусом " << r << " площадь = "

<< (*p) (r)

 

 

<< endl;

 

/* Демонстрируем, как об-

ратиться к

 

 

 

 

функции через указатель

 

 

*/

system("pause");

 

// Результат одинаков!

return 0;

 

 

}

 

 

57

Рис. 9.2. Результат работы программы по вычислению площади круга

Указатель на объект

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

тип *имя:

где тип может быть любым, кроме ссылки и битового поля. Звез-

дочка относится непосредственно к имени, поэтому для того, что-

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

Например, в операторе

int *a, n, *с;

описываются два указателя на целое с именами а и с, а также целая переменная b. Размер указателя зависит от модели памяти. Можно определить указатель на указатель (при необходимости) [10].

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

Указатель на void применяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов). Указателю на void можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом (лучше всего это делать через static_cast). Эта операция позволяет преобразовать переменную или указатель одного типа в другой. Ее синтаксис: static_cast <новый тип> (переменная).

58

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

Пример:

int a = 7;

 

void *p = &a;

// p – указатель

 

/* !Внимание! cout<<

 

*ptr <<endl;

 

нельзя разыменовывать

 

указатель типа void */

int *p = static_cast<int*> (p); /* Однако если мы

 

конвертируем наш

 

указатель void в

 

указатель int */

cout << *p << endl;

/* То мы сможем его

 

разыменовать,

 

подобно обычному

 

указателю*/

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

Рассмотрим примеры:

int n;

// Целая переменная

const int cni = 1;

// Целая константа

int *pni ;

// Указатель на целую переменную

const int *pcni;

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

int *const ср = &n;

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

 

переменную */

const int *const срс = &cni;

/* Указатель-константа

 

на целую

 

константу */

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

59

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

Указатели чаще всего используют при работе с динамической памятью (ее иногда называют кучей). Это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, производится только через указатели. Время жизни динамических переменных – от точки создания до конца программы или до явного освобождения памяти. В C++ используется два способа работы с динамической памятью. Первый использует семейство функций malloc и достался в наследство от С, второй использует операции new и delete. Стоит учесть, что использование malloc требует подключения протокола <malloc.h>.

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

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

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

– с помощью операции получения адреса:

int а = 5; // Целая переменная

int* р = &а; // В указатель записывается адрес а int* р (&а): // То же самое другим способом

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

int* г = р;

– с помощью имени массива или функции, которые трактуются как адрес:

int mas[10];

//

Массив

int *t = mas;

//

Присваивание адреса начала

 

 

массива

void f(int a ){ /* … */ }; //

Определение функции

void (*pf)(int);

//

Указатель на функцию

pf = f;

//

Присваивание имени функции

60

Соседние файлы в папке книги