Доброй ночи
Я изучаю C++ для разработки научного кода. В отличие от моей стратегии обучения, используемой в python и MATLAB, я пытаюсь изучить C++ старым и глубоким способом (используя книгу ref [1]). Я изо всех сил пытаюсь понять указатели и динамическое распределение памяти.
Я наткнулся на это упражнение, и появились некоторые сомнения. Упражнение представляет собой простое умножение матрицы 2x2. Код:
#include<iostream>
//Perform C[2][2] = A[2][2]*B[2][2] Using dynamic memory allocation
int main(){
//declaring length of the matrices
int row,col;
row = 2;
col = 2;
//Allocating memory for matrice A [heap ?]
double** A, **B, **C;
A = new double* [row];
B = new double* [row];
C = new double* [row];
for (int i=0; i<row; ++i){
A[i] = new double [col];
B[i] = new double [col];
C[i] = new double [col];
}
//Performing calculation
for (int i=0; i<2; ++i){
for (int j=0; j<2; ++j){
A[i][j] += 10;
B[i][j] += 20;
C[i][j] += A[i][j]*B[i][j];
std::cout << C[i][j] << " ";
}
std::cout << std::endl;
}
//Deleting memory allocation [Is there an memory leak ? (No *=NULL)]
for (int i=0; i<2; ++i){
delete[] A[i];
delete[] B[i];
delete[] C[i];
}
delete[] A;
delete[] B;
delete[] C;
return 0;
}
Мои сомнения:
1 - Я читал о разделении памяти (global/stack/heap), где они находятся в аппаратном обеспечении? Кэш HD/RAM/CPU?
2 - когда я выделяю массив:
int arrayS[2]; //Is this at the stack ?
int* arrayH; //Is this at the heap ?
arrayH = new int [2];
3 - В моем решении матричной задачи. Есть ли утечка памяти или создание мусора? (обратите внимание, что я не указывал массивы на * = NULL, чтобы избавиться от адреса)
4 - Вы предлагаете какой-либо способ повысить эффективность и эффективность моего кода?
Спасибо !
В c++ объект может быть расположен в стеке и в куче. В вашем примере int arrayS [2] находится в стеке funcion, в котором он был объявлен. Если вы хотите создать объект в куче (доступная память ко всем функциям, которые существуют в программе), вы должны использовать оператор new. Указатель, который хранит адрес выделенного объекта (в вашем случае arrayH), также нажимает на стек функции, в которой он объявлен. На самом деле память один процесс (исполняемая программа) погружается в три части:
Область кода (область, в которой находится код программы);
Область стека (начинается в месте, где указатель Stack-Pointer указывает на память. Указатель стека - это регистр, который хранит каждый раз адрес текущего стека. В программе больше одного стека за один раз это зависит от уровня рекурсивности вашей программы)
Область данных (это глобальная память, доступная из всей программы. Она выделена в c++ только с помощью нового оператора или глобальной переменной)
Он существует также shared_memory, который позволяет программисту выделять память, доступную более чем в одном процессе.
** В вашем коде отсутствует утечка памяти. Но в c++ реализована концепция smart_pointer, которая очень похожа на габаб-колектор из С# и Java **
Также c++ является языком программирования ООП, поэтому вы обязательно должны писать свой код, используя классы, наследование, полиморфизм и т.д.
std::make_shared
. (Конечно, под капотом, вероятно, будет использоваться operator new
.)
Я читал о разделении памяти (global/stack/heap), где они находятся в аппаратном обеспечении? Кэш HD/RAM/CPU?
Они могут быть сохранены везде, где ваша программа C++ решит, что они должны быть. Некоторые локальные переменные, вероятно, существуют только в регистрах, если они имеют короткий срок службы и никогда не имеют указателя на них.
Вероятность того, что все остальное произойдет где-то в системной ОЗУ (которая может быть выгружена на диск, если ваш процесс несчастлив). Разница в том, как они используются программой, а не там, где они есть. Где они являются деталью реализации, о которой вам действительно не нужно беспокоиться.
когда я выделяю массив
Да, ваш анализ стека против кучи правильный. Ваш первый (int arrayS[2];
) имеет оговорку, что если вы объявите эту переменную как часть класса или структуры, она может существовать в любом месте в зависимости от того, как создается класс/структура. (Если класс/структура создается в куче, тогда массив будет находиться в куче, если он создается в стеке, а затем в стеке.)
И, конечно, если он глобальный, то он имеет глобальное хранилище.
Вы предлагаете какой-либо способ повысить эффективность и эффективность моего кода?
Инкапсулируйте концепцию матрицы внутри класса и используйте std::vector
вместо выделения собственных массивов. Объем косвенности будет одинаковым после оптимизации компилятора, но вам больше не придется заниматься управлением памятью, используемой массивами.
1) Глобальная область, стек и куча расположены в вашем адресном пространстве процессора приложений. Это будет означать, что они находятся в ОЗУ, при условии подкачки по виртуальной памяти (в этом случае они могут пойти и жить на HD на некоторое время) и кэширование ЦП (в этом случае они могут некоторое время переходить на CPU). Но обе эти вещи являются прозрачными по отношению к сгенерированному коду.
2) да, arrayS[2]
будет в стеке (если это не глобально, конечно), и все, что возвращается new
(или malloc
, если вы включите некоторый код C), находится в куче.
3) Я не вижу каких - либо утечек, но если вы собираетесь использовать row
и col
, а не повторять 2
волшебную константу во всем месте, то сделать это равномерно и помечать их как const
, так что вы не можете случайно изменить их.
4) с точки зрения эффективности кеша, может быть лучше сделать одно размещение как new double[col * row]
а затем либо распространить ваши указатели на весь этот блок, либо индексировать как [i*col + j]
, предполагая, что i
индексы строки и все строки являются col
элементами длиной. new
в противном случае разрешено распространять ваши строки, где бы они ни находились в памяти, что, скорее всего, приведет к ошибкам кэша.
Что касается стиля? Ну, вы не спрашивали, но я думаю, что комментарий cdhowie действителен. Также следует глубже рассмотреть точку дедупликатора: у STL есть куча штук, чтобы убедиться, что вы не просачиваете память - читайте на unique_ptr
и shared_ptr
.
std::vector
будет намного легче понять правильно и намного легче правильно использовать повторно. И при достаточной оптимизации компилятора он не будет работать хуже.std::unique_ptr
чтобы сохранить вашу память.