Вот я читаю, пишу, как надо, а оно не компилируется! Кто виноват, тупой читатель или хреновый автор? Я не считаю Страуструпа хреновым, но, если бы, книга была толще в 10 раз, и каждая тема капитально разжевывалась, и все объяснялось на пальцах, и было бы очень много примеров, желательно, отдельная книга с примерами, то мне было бы гораздо легче изучить C++.
Не слушай плюсовиков-затейников, они оторвались от природы и уже два числа сложить не могут без перегрузки операторов и шаблонов. Тут дел то на пять минут.
A multidimensional array should be organized so that the last index changes fastest. This makes sure that the elements are accessed sequentially. The opposite order of the two loops would make the access non-sequential which makes the data caching less efficient.
A matrix or multidimensional array should be stored in one contiguous memory block. Do not use one container for each row or column. The access is faster if the number of elements per row is a constant known at compile time.
implementing a matrix in STL as a vector of vectors, as is often seen, is certainly a very inefficient solution.
Так как-то спокойнее на душе, физический смысл нагляднее - массив из указателей длина такая-то. Лишнее напоминание что это за фигня такая А, способ впасть в особое состояние духа, типа поедания мухоморов перед боем или дыхательной гимнастики. Ну ты понел.
Ну вообще-то это делается так же как и в C.
Поэтому всё же изучая C++ лучше начинать с C.
Если массив динамический - значит память нужно выделять «вручную» то есть программно.
На C это примерно так делается:
#include <malloc.h>
#include <math.h>
#include <stdio.h>
typedef struct { int m, n; double *e; } matrix;
matrix *matrix_create(int m, int n);
void matrix_free(matrix *a);
int matrix_set(matrix *a, int i, int j, double v);
int matrix_get(matrix *a, int i, int j, double *v);
// Создание матрицы
matrix *matrix_create(int m, int n) {
matrix *a = NULL;
if (m > 0 && n > 0 && ((a = (matrix *) malloc(sizeof(matrix))) != NULL)) {
a->m = m;
a->n = n;
if ((a->e = (double *) malloc(sizeof(double) * m * n)) != NULL)
for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) matrix_set(a, i, j, 0);
else { free(a); a = NULL; }
}
return a;
}
// Освобождение матрицы
void matrix_free(matrix *a) { free(a->e); free(a); }
// Присвоение значения элементу матрицы
int matrix_set(matrix *a, int i, int j, double v) {
int ret = i > 0 && j > 0 && i <= a->m && j <= a->n;
if (ret) *(a->e + (i - 1) * a->n + j - 1) = v;
return ret;
}
// Получение элемента матрицы
int matrix_get(matrix *a, int i, int j, double *v) {
int ret = i > 0 && j > 0 && i <= a->m && j <= a->n;
if (ret) *v = *(a->e + (i - 1) * a->n + j - 1);
return ret;
}
// Пример использования
int main(void) {
double v;
matrix *a = matrix_create(3, 3);
matrix_set(a, 1, 1, M_PI);
matrix_set(a, 2, 2, 2 * M_PI);
matrix_set(a, 3, 3, M_PI * M_PI);
for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) {
matrix_get(a, i, j, &v);
printf("%f%s", v, j == 3 ? "\n" : " ");
}
matrix_free(a);
return 0;
}
Переделать это в класс C++ в общем-то не так сложно.
Куда важнее понять принцип работы с памятью или с динамическими массивами в данном случае.
Для того что бы уж совсем «динамической» матрицу сделать к тем функциям которые я привел нужно добавить ещё одну, что-то вроде matrix_recreate.
Но это ты уж сам. Домашнее задание так сказать) Подсказка — нужна функция realloc.
И после этого тред можно считать закрытым а то развели флейм.
if ((a->e = (double *) malloc(sizeof(double) * m * n)) != NULL) for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) matrix_set(a, i, j, 0); else { free(a); a = NULL; }
if (!(a->e = calloc(m * n, sizeof(double)))) { free(a); a = NULL; }
Вместо int должно быть size_t и индексация с нуля — проверки на > 0 и вычитания единиц не нужны.
В c99 есть flexible array members — matrix_free не нужен.
В си есть хороший сахар для *(ptr + offset) :)
Приведения (void*) -> (T*) не нужны.
Ещё я бы добавил const и заменил проверки на assert (так что возвращение double у matrix_get).
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct { size_t m, n; double e[]; } matrix;
matrix* matrix_create(const size_t m, const size_t n)
{
matrix *a = NULL;
if ((a = calloc(sizeof(matrix) + m * n * sizeof(a->e[0]), 1))) {
a->m = m; a->n = n;
}
return a;
}
void matrix_set(matrix *const a, const size_t i, const size_t j, const double v)
{
assert(i < a->m && j < a->n);
a->e[i * a->n + j] = v;
}
double matrix_get(const matrix *const a, const size_t i, const size_t j)
{
assert(i < a->m && j < a->n);
return a->e[i * a->n + j];
}
#define M_PI 3.14159265358979323846
int main()
{
matrix *const a = matrix_create(3, 3);
matrix_set(a, 0, 0, M_PI);
matrix_set(a, 1, 1, M_PI * M_PI);
matrix_set(a, 2, 2, M_PI * M_PI * M_PI);
for (size_t i = 0; i < 3; ++i)
for (size_t j = 0; j < 3; ++j)
printf("%f%s", matrix_get(a, i, j), j == 2 ? "\n" : " ");
free(a);
}
Главное человека не запутать.
А так да. Хорошо.
Ну что, теперь ждем matrix_recreate (а лучше matrix_resize)? :)
Но это puding уже сам пусть попробует сделать.
Иначе как-то без изменения размерности матрицы (читай двухмерного массива) это и не динамика как бы.
Ну так, приближение только к ней.
Двухмерный массив есть, но от адреса на него правда почти ушли и динамика как бы на половину.
vector это не вектор (с) Степанов :) С resize это «динамический массив», соответственно, динамический массив динамических массивов (может быть ещё и разного размера) это не вполне матрица.
А «динамичность» вектора или матрицы тут в неизвестности m и n (антипод — статичность, то есть известность их во время компиляции).
1. Из того что приводил ТС непонятно нужен ли ему resize, но я видел известность m и n — может хватит простого T[m][n] или std::arraty<std::array<T, m>, n> которые передаются элементарно по значению указателя для массива и по значению (если нужно копирование) или по ссылке для std::array.
2. Если m и n неизвестны (постоянны или нет с resize) — лёгкий класс Matrix будет минимальным заголовком (begin, m, n или begin, end, m/n) который будет накатываться конструктором на стеке и делать ту же единственную аллокацию куска в куче и указывать на неё. Передаётся по ссылке (тут будет два разыменования вместо одного во варианте с flexible array members) или по значению (с конструктором копирования). Стандартные std::string и std::vector обычно так устроены — в гнутой реализации одно это слово {begin}, другое — три слова {begin, end, capacity_end}.
3. Если это jagged array — std::vector<std::vector<T>>. Передаётся так же — по ссылке или по значению с копированием.
Понаписали то сколько изверги. А надо было просто сказать в ответ ТС'овский на код
const int QwMatrixWidth = 20;
const int QwMatrixHeight = 20;
QwMatrixElement QwMatrix[QwMatrixHeight][QwMatrixWidth];
void QwDrawMatrix(QwMatrixElement** matrix, int n, int m) {
//Производятся операции над элементами матрицы
}
что двумерный массив и указатель на указатель - не эквивалентные и не «взаимотрансформируемые» вещи.
ТС, почитай где нибудь, что в C/C++ есть два основных типа памяти под используемые объекты - стек и хип. То, что в стеке, создается автоволшебно, то что в хипе создается через new/*alloc и достается через указатели.
Свою матрицу ты создал в стеке, так что она уже заполнена, и в памяти лежит последовательно. То есть массив 2x3 лежит в памяти так
Один из методов делать итерацию по указателю на указатель (далее УнУ) такой:
// где-то ранее инициализироан int **pp ;
for (int i = 0; i<rows; i++ ){
for (int j=0; j<columns; j++) {
//pp {+X} - указатель на строку
//*pp {+Y}- указатель на столбец
//**pp - значение в ячейке
std::cout << *(*(pp+i)+j) << " " ;
}
std::cout << "\n";
}
Теперь представь, ты передал в функцию свою матрицу приведя ее тип к УнУ. В этом случае в pp будет адрес ячейки row0col0, а вот в *pp уже будет значение ячейки row0col0, например -10. Поэтому когда ты попробуешь обратиться к **pp, то программа попробует прочитать память по адресу -10, в результате чего встретится с отцом и великим повелителем всех программистов С/С++ - сигналом №11, более изветсном в Рунетах как «Ошибка сегментации».
Короче, чтобы у тебя заработало в твоих изначальных условиях передавай указатель, а не УнУ. Пример:
#include <iostream>
class CC {
public:
int aa;
CC() {aa=10;};
};
void drawCC (CC* cc, int rows, int columns) {
for (int i = 0; i<rows; i++ ){
for (int j=0; j<columns; j++) {
std::cout << "aa:" << (cc+i*rows+j)->aa+10*i+j << " " ;
}
std::cout << "\n";
}
}
int main(int argc, char **argv)
{
int rows;
int columns;
std::cin >> rows >> columns ;
CC cc[rows][columns];
drawCC(reinterpret_cast<CC*> (&cc),rows,columns);
return 0;
}
ЗЫ: Да, из C++ здесь только class, iostream и reinterpret_cast, остальное чистый С, но с ним всегда так - говоришь Партия, подразумеваешь Ленин и т.д.
Нельзя :) Вот пример в котором 1) есть хоть какие-то соглашения о стиле, 2) работает для тензоров любого ранга, 3) более типобезопасен, то есть нельзя сделать t(1, 2, 3, 4) для тензора третьего ранга или недодать размеров тензору, например, 4) работает в пару-другую раз быстрее (это как я замерил), 5) и опять выделяет память компактно один раз — расходуется её в пару-другую-третью раз меньше и с отсутствием роста pagefault-ов (так time сказал).
1) Стиль «нечитаемая шняга» :D
2) не является уникальной фичей
3) не факт
4) не критично,скорость написания программ для которых это проявится (или не проявится) во много раз меньше скорости роста производительности процессоров
5) Стратегия выделения памяти делается конкретно под задачу переписыванием new
6) Не компиляется, школьники негодуют.
7) И где тут «классический» вид x[j][k] ?
Нету. Есть operator() с верхним ограничением на количество индексов. А у тебя можно x[1][2][3][4][... и дальше сколько угодно раз для любого x который TMATRIX.
А в чем криминал совмещения malloc и new ?
А в чем криминал вычисления размера указателя через void*, другой тип вернет какой-то другой размер ?
А в чем криминал знакового типа для определения требуемой памяти, у меня 500 мегов оперативки, мне и int за глаза хватает.
И где там память нулевого размера ? Ты книжки почитай, в любой книжке проверки убирают, чтобы мысль не терялась среди проверок. Освобождение памяти сам допишешь. val не лишний, а зарезервированный, row лишнего нет.
Писать тебе, причём в compile-time — у меня уже написано и работает.
Что характерно — указатели/массивы си и стандартные контейнеры C++ не позволят с ними так обращаться (многократно вызывать operator[]), и это compile-time гарантии.
Считай домашним заданием.
Как раз твой код недвусмысленно намекает на таковое — при трудоустройстве исправят, думаю, если ты такой Ъ, что всё «знаешь» («скорости роста производительности процессоров», ага, типа наплевательское отношение к иерархии памяти не сказывается всё больше и больше) и не слушаешь что тебе тут говорят.
Лорчую, специально грепал за этим советом.
Только разве не typedef std::vector<std::vector<Type> > Array; ?
(добавился пробел)
Уверен, что ОП экономит на спичках.
Мне это не нужно, банально не за это платят деньги, так получилось. И тема не про производительность, а про простейший пример массива. Ты из пушки по воробьям стреляешь.
Ну ты сам ответил как надо быстро - выделить кусок и вычислять индекс, желательно без операции умножения. А шаблоны, С++, красивые записи [x][y][z] это понты.
А, ну извини :) Но твой пример всё равно запутанный — достаточно просто new[] один раз для TMATRIX *rows и placement new по кускам, мой другой пример тоже прост — static_product мог быть парой строчек for (с той же производительностью), но специально сделано через шаблонную рекурсию чтобы для недопустимого количества индексов она не компилировалась.