Главная страница

Не зависят от конкретного компьютера Язык Си


Скачать 5.8 Mb.
НазваниеНе зависят от конкретного компьютера Язык Си
Дата24.10.2022
Размер5.8 Mb.
Формат файлаppt
Имя файлаINFORMATIKA_lektsii_IB-1.ppt
ТипПрограмма
#751529
страница11 из 16
1   ...   8   9   10   11   12   13   14   15   16
void *, которую можно привести к любому необходимому типу указателя.
Содержание выделенного блока памяти не инициализируется, оно остается с неопределенными значениями. За счет этого программа работает быстрее.
Для работы с функциями calloc() и malloc() требуется библиотека .





Динамические массивы


Память выделяется из сектора оперативной памяти, доступного для любых программ, выполняемых на данном компьютере.
Если памяти недостаточно, чтобы удовлетворить запрос, функции calloc(), malloc() возвращают нулевой указатель.
Важно всегда прове­рять возвращаемое значение на его равенство NULL, прежде чем использовать этот указатель.
Попытка использования нулевого указателя обычно влечет крах системы.





Динамические массивы


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





Динамические массивы


Примеры шаблонов динамического выделения памяти:
функция calloc()
int *имя_указателя, n, m;
имя_указателя = (int *) calloc(n*m, sizeof(int));
или
int n, m;
тип_данных *имя_указателя = (тип_данных *) calloc(n*m, sizeof(тип_данных));





Динамические массивы


функция malloc()
int *имя_указателя, n, m;
имя_указателя = (int *) malloc(n*m*sizeof(int));
или
int n, m;
тип_данных *имя_указателя = (тип_данных *) malloc(n*m*sizeof(тип_данных));





Динамические массивы


При динамическом выделении памяти, как с помощью функции calloc(), так и с помощью функции malloc(), указателю имя_указателя присваивается адрес участка памяти, размер которого соответствует типу данных тип_данных.
Автоматически, этот участок памяти становится недоступным для других программ. Это значит, что после того, как выделенная память станет ненужной, её нужно явно высвободить.





Динамические массивы


Если память не будет явно высвобождена, то по завершении работы программы память так и не освободится для операционной системы, это называется утечкой памяти.
Выделение памяти возможно под любой тип данных: int, float, double, char и т.д.





Динамические массивы


Также можно определять размер выделяемой памяти, которую нужно выделить, передавая пустой указатель.
Пример:
int *ptrvalue = malloc(sizeof(*ptrvalue));
Операция sizeof(*ptrvalue) оценит размер участка памяти, на который ссылается указатель. Так как ptrvalue является указателем на участок памяти типа int, то sizeof() вернет размер целого числа. То есть, по первой части определения указателя, вычисляется размер для второй части.





Динамические массивы


Это может понадобиться, если появится необходимость поменять определение указателя int, например, на float.
Тогда, не нужно менять тип данных в двух частях определения указателя. Достаточно только поменять первую часть:
float *ptrvalue = malloc(sizeof(*ptrvalue));
В такой записи мы не вызываем функцию malloc() с использованием sizeof(float). Вместо этого в malloc() передается указатель на тип float.
В таком случае размер выделяемой памяти автоматически определится сам.





Динамические массивы


Высвобождение блока памяти, ранее выделенной с помощью функции calloc() или malloc() выполняется с помощью функции free().
Синтаксис:
void free (void *имя_указателя size);
имя_указателя – указатель на область памяти, ранее выделенную с помощью функции calloc() или malloc().
Если в качестве аргумента передается нулевой указатель, никаких действий не происходит.





Динамические массивы


Обратите внимание, что функция free() оставляет значение имя_указателя неизменным. Следовательно, даже после высвобождения памяти указатель все равно указывает на тот же блок памяти.
Поэтому, после освобождения памяти рекомендуется обнулить указатель, то есть присвоить указателю 0.
Запись * ptrvalue = 0 означает, что указатель ptrvalue становится нулевым, другими словами, он уже никуда не указывает.





Динамические массивы


Все элементы двумерного массива хранятся в одномерном массиве размером NxM элементов.
Сначала в этом массиве расположена 0-я строка матрицы, затем 1-я и т. д. Поэтому для обращения к элементу A i, j необходимо по номеру строки i и номеру столбца j вычислить номер элемента k в динамическом массиве.
Учитывая, что в массиве элементы нумеруются с нуля, k = i * M + j.
Обращение к элементу A[i][j] можно выполнить как *(имя_указателя + i * M + j).





Динамические массивы


Пример использования функции malloc()
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
setlocale(LC_ALL,"Rus");
int *ptrvalue, i, j, N, M;





Динамические массивы


// ввод размерности массива
cout << "Количество строк массива = ";
cin >> N;
cout << "Количество столбцов массива = ";
cin >> M;
cout << endl;
//выделение памяти под массив из NxM элементов
ptrvalue = (int *) malloc(N * M * sizeof(int));
//если выделение памяти не выполнено, завершить
if (ptrvalue == NULL) exit (1);





Динамические массивы


// ввод диапазона рандомизации
int a, b;
cout << "Нижняя граница рандомизации = ";
cin >> a;
cout << "Верхняя граница рандомизации = ";
cin >> b;
cout << endl;
// заполнение массива случайными числами из диапазона [a,b]
srand(time( NULL ));
if (a > b) {int r = a; a = b; b = r;}
for (i = 0; i < N; i++)
for (j = 0; j < M; j++)
*(ptrvalue + i * M + j) = rand()%(b - a + 1) + a;





Динамические массивы


// вывод массива на экран
for (i = 0; i < N; i++) {
for (j = 0; j < M; j++)
cout << setw(5) << *(ptrvalue + i * M + j) << " |";
cout << endl;
}
cout << endl;
// высвобождение памяти
free(ptrvalue);
_getch();
return 0;
}





Динамические массивы


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





Динамические массивы


Пример использования функции calloc(). Задана матрица A(N,M). Требуется поменять местами ее максимальный и минимальный элементы.

int main()
{
int *ptrvalue, i, j, N, M, buffer;
int imax = 0, jmax = 0, imin = 0, jmin = 0;
int min, max;
… // ввод размерности массива
ptrvalue = (int *) calloc(N * M, sizeof(int));
// если выделение памяти не выполнено, завершить программу
if (ptrvalue == NULL) exit (1);





Динамические массивы


… // заполнение массива случайными числами из диапазона [a,b] и вывод его на экран
// преобразование массива
max = min = *ptrvalue;
for (i = 0; i < N; i++)
for (j = 0; j < M; j++) {
if ((*(ptrvalue + i * M + j)) > max)
{ max = *(ptrvalue + i * M + j); imax = i; jmax = j; }
if ((*(ptrvalue + i * M + j)) < min)
{ min = *(ptrvalue + i * M + j); imin = i; jmin = j; }
}
buffer = *(ptrvalue + imax * M + jmax);
*(ptrvalue + imax * M + jmax) = *(ptrvalue + imin * M + jmin);
*(ptrvalue + imin * M + jmin) = buffer;





Динамические массивы


// вывод преобразованного массива на экран
for (i = 0; i < N; i++) {
for (j = 0; j < M; j++)
cout << setw(5) << *(ptrvalue + i * M + j) << " |";
cout << endl;
}
cout << endl;
// высвобождение памяти
free(ptrvalue);
*ptrvalue = 0;

}





Динамические массивы


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





Динамические массивы


Оператор new создает объект заданного типа, выделяет ему память и возвращает указатель заданного типа на данный участок памяти.
Синтаксис:
тип_данных *имя_указателя = new тип_данных;
С помощью оператора new может быть выделена память под любой тип данных: int, float, double, char и т.д.





Динамические массивы


Пример шаблона динамического выделения памяти с использованием оператора new:
int *имя_указателя, n, m;
имя_указателя = new int [n*m];
или
int n, m;
тип_данных *имя_указателя = new тип_данных [n*m];





Динамические массивы


Если операция выделения памяти не может быть выполнена, например, в случае отсутствия свободных участков, то возвращается нулевой указатель.
Выделяемая память после еe использования должна высвобождаться, поэтому операторы new и delete используются парами.
Оператор delete высвобождает память, ранее выделенную оператором new.





Динамические массивы


Пример шаблона высвобождения памяти с использованием оператора delete:
delete [ ] имя_указателя;
имя_указателя – указатель на участок памяти, ранее выделенный с помощью оператора new.
Обратите внимание. При использовании оператора delete для указателя префикс * не используется.





Динамические массивы


Так как оператор new выделил память под размещение массива, то и при еe высвобождении надо дать понять компилятору, что необходимо освободить память массива, а не только его нулевой ячейки, на которую указывает A.
Поэтому между оператором delete и именем указателя ставятся квадратные скобки [ ].





Динамические массивы


Пример использования оператора new. Заданы две матрицы A(N,M) и B(N,M). Требуется вычислить матрицу C = A + B.

int main()
{
setlocale(LC_ALL,"Rus");
int i, j, N, M;
int *ptrarray_A, *ptrarray_B, *ptrarray_C;
… // ввод размерности массива
// выделение памяти для матриц
ptrarray_A = new int [N * M];
ptrarray_B = new int [N * M];
ptrarray_C = new int [N * M];





Динамические массивы


… // заполнение матриц А, B случайными числами из диапазона [a,b] и вывод их на экран
// вычисление матрицы C = A + B
for (i = 0; i < N; i++)
for (j = 0; j < M; j++)
*(ptrarray_C + i * M + j) = *(ptrarray_A + i * M + j) + *(ptrarray_B + i * M + j);
// вывод матрицы C на экран
for (i = 0; i < N; i++) {
for (j = 0; j < M; j++)
cout << setw(5) << *(ptrarray_C + i * M + j) << " |";
cout << endl;
}
cout << endl;





Динамические массивы


// высвобождение памяти
delete [ ] ptrarray_A;
delete [ ] ptrarray_B;
delete [ ] ptrarray_C;

}





Динамические массивы


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





Динамические массивы


2-й способ работы с динамическими матрицами основан на использовании двойного указателя – указателя на указатель.





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


Указатели могут ссылаться на другие указатели. При этом в ячейках памяти, на которые ссылаются первые указатели, содержатся не значения, а адреса вторых указателей.
Например,
int **ptr;
это указатель на ptr, или массив указателей, которые указывают на адреса участков памяти с данными типа int.
Каждый элемент этого массива – это указатель, который содержит в себе адрес строки (первого элемента массива ptr).





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





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


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





Динамические массивы


Пример шаблона динамического выделения памяти для динамической матрицы:
int n, m;
тип_данных **имя_указателя;
имя_указателя = new тип_данных [n];
for (i = 0; i < n; i++) имя_указателя[i] = new тип_данных[m];
тип_данных **имя_указателя – указатель на указатель, который содержит адрес массива указателей на строки, состоящий из n элементов типа тип_данных.





Динамические массивы


Для определения массива указателей на строки организован цикл от 0 до n-1, в котором каждый из n указателей выделяет участок памяти для хранения m элементов типа тип_данных.
В результате создается динамическая матрица размера NxM.
Обращение к элементу динамической матрицы проходит так же, как и к элементу статической матрицы.





Динамические массивы


Высвобождение памяти, отводимой под двумерный динамический массив, реализуется в 2 этапа.
Обратите внимание. Сначала с помощью цикла высвобождается память из-под строк, на которую ссылаются указатели из массива указателей. Затем осуществляется высвобождение памяти, выделенной под сам массив указателей.
for (int i = 0, i < n; i++)
delete [ ] имя_указателя[i];
delete [ ] имя_указателя;





Динамические массивы


Пример. Задана матрица A(N,M). Требуется исключить из нее строку r.

int main()
{
setlocale(LC_ALL,"Rus");
int i, j, N, M;
int **ptrarray;
… // ввод размерности массива
// выделение памяти под массив указателей на строки
ptrarray = new int *[N];
// выделение памяти под строки массива указателей
for(i = 0; i < N; i++)
ptrarray[i] = new int [M];





Динамические массивы


… // заполнение матрицы случайными числами из диапазона [a,b] и вывод ее на экран
// определяем удаляемую строку
int r;
cout << "Номер удаляемой строки = ";
cin >> r;
cout << endl;
//проверяем наличие строки r в динамическом массиве
if (r < 0 || r >= N)
{
cout << "Строки с номером " << r << " нет" << endl;
_getch();
exit (1);
}





Динамические массивы


// выделяем память под временную копию массива указателей без строки r
int **ptrcopy;
ptrcopy = new int *[N - 1];
// копируем адреса указателей, кроме строки r
int k = 0;
for(int i = 0; i < N; i++)
if (i != r) {ptrcopy[k] = ptrarray[i]; k++;}
// удаляем строку r
else {delete [] ptrarray[i];}
// удаляем исходный массив указателей
delete [] ptrarray;
// изменяем значения параметров массива на новые
ptrarray = ptrcopy;
N--;





Динамические массивы


// вывод преобразованной матрицы на экран
cout << "Преобразованная матрица" << endl << endl;
for (i = 0; i < N; i++) {
for (j = 0; j < M; j++)
cout << setw(5) << ptrarray[i][j] << " |";
cout << endl;
}
cout << endl;
// высвобождение памяти из-под строк массива указателей
for (i = 0; i < N; i++)
delete [] ptrarray[i];
// высвобождение памяти из-под массива указателей
delete [] ptrarray;

}


1   ...   8   9   10   11   12   13   14   15   16


написать администратору сайта