Перейти к содержимому

Как передать двумерный вектор в функцию c

  • автор:

Двумерные массивы

Для хранения прямоугольных таблиц, состоящих из строк и столбцов, необходимо использовать массивы, каждый элемент которых является строкой массивом чисел. Если считать, что одна строка — это vector , то двумерный массив — это вектор элементов типа vector , то есть его нужно объявлять как vector . При этом по стандарту языка C++ до 2011 года, в конце определения между двумя символами “

Если объявить массив a таким образом, то a[i] будет одномерным массивом, который обычно считают строкой. То есть a[i][j] будет j-м элементом i-й строки. Например, двумерный массив из 3 строк и 4 столбцов можно записать в виде:

a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]

Чтобы создать массив из n строк и m столбцов, можно объявить его указанным образом:

vector > a;

Затем необходмио размер “внешнего” массива изменить на n (сделать n строк в таблице):

a.resize(n);

Затем размер каждого массива-строки необходимо изменить на m. Это можно сделать циклом:

for (int i = 0; i < n; ++i) a[i].resize(m);

Заметим, что строки могут иметь разную длину, например, можно сделать массив в форме “лесенки”, где каждая строка будет содержать на один элемент больше предыдущей:

for (int i = 0; i < n; ++i) a[i].resize(i + 1);

Но если необходимо создать прямоугольный массив, то можно сразу же при объявлении задать его размер, если воспользоваться конструктором для вектора с параметрами. Первый параметр — размер вектора, второй необязательный параметр — значение, которым будут инциализированы элементы вектора. Тогда в качестве первого параметра можно передать n, а в качестве второго параметра можно явно указать конструктор, который создает вектор из m элементов типа int : vector(m) . Итак, создать прямоугольную таблицу размером n×m можно в одну строку:

vector > a(n, vector(m));

Если вложенному вызову конструктора (для строки) передать второй параметр, то все элементы массива будут заполнены переданным значением вместо нуля. int A[n] создает в памяти одномерный массив: набор пронумерованных элементов, идущих в памяти последовательно. К каждому элементу массива можно обратиться, указав один индекс - номер этого элемента. Но можно создать и двумерный массив следующим образом: int A[n][m] . Данное объявление создает массив из n объектов, каждый из которых в свою очередь является массивом типа int [m] . Тогда A[i] , где i принимает значения от 0 до n-1 будет в свою очередь одним из n созданных обычных массивов, и обратиться к элементу с номером j в этом массиве можно через A[i][j] .

Подобные объекты (массивы массивов) также называют двумерными массивами. Двумерные массивы можно представлять в виде квадратной таблицы, в которой первый индекс элемента означает номер строки, а второй индекс – номер столбца. Например, массив A[3][4] будет состоять из 12 элементов и его можно записать в виде

A[0][0] A[0][1] A[0][2] A[0][3] A[1][0] A[1][1] A[1][2] A[1][3] A[2][0] A[2][1] A[2][2] A[2][3]

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

for (int i = 0; i < n; ++i) < // Выводим на экран строку i for (int j = 0; j < m; ++j) < cout cout 

А считать двумерный массив с клавиатуры можно при помощи еще более простого алгоритма (массив вводится по строкам, то есть в порядке, соответствующему первому примеру):

for (i = 0; i < n; ++i) < for (j = 0; j < m; ++j) < cin >> a[i][j]; > >

Обработка двумерного массива

Обработка двумерных массивов производится аналогичным образом. Например, если мы хотим записать в массив таблицу умножения, то есть присвоить элементу a[i][j] значение i * j , это можно сделать следующим образом при помощи вложенных циклов:

for (i = 0; i < n; ++i) < for (j = 0; j < m; ++j) < a[i][j] = i * j; >>

Рассмотрим более сложную задачу и несколько способов ее решения. Пусть дан квадратный двумерный массив размером n×n. Необходимо элементам, находящимся на главной диагонали проходящей из левого верхнего угла в правый нижний (то есть тем элементам a[i][j] , для которых i == j ) присвоить значение 1 , элементам, находящимся выше главной диагонали – значение 0, элементам, нахощящимся ниже главной диагонали – значение 2. То есть получить такой массив (пример для n == 4 ):

1 0 0 0 2 1 0 0 2 2 1 0 2 2 2 1

Рассмотрим несколько способов решения этой задачи. Элементы, которые лежат выше главной диагонали – это элементы a[i][j] , для которых i < j , а для элементов ниже главной диагонали i >j . Таким образом, мы можем сравнивать значения i и j и по ним определять значение a[i][j] . Получаем следующий алгоритм:

for (i = 0; i < n; ++i) < for (j = 0; j < n; ++j) < if (i < j) < a[i][j] = 0; >else if (i > j) < a[i][j] = 2; >else < a[i][j] = 1; >> >

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

Сначала заполним главную диагональ, для чего нам понадобится один цикл:

for (i = 0; i

Затем заполним значением 0 все элементы выше главной диагонали, для чего нам понадобится в каждой из строк с номером i присвоить значение элементам a[i][j] для j = i+1 , . n-1 . Здесь нам понадобятся вложенные циклы:

for (i = 0; i < n; ++i) < for (j = i + 1; j < n; ++j) < a[i][j] = 0; >>

Аналогично присваиваем значение 2 элементам a[i][j] для j = 0 , . i-1 :

for (i = 0; i < n; ++i) < for (j = 0; j < i; ++j) < a[i][j] = 2; >>

Можно также внешние циклы объединить в один и получить еще одно, более компактное решение:

for (i = 0; i < n; ++i) < // Заполняем строку с номером i for (j = 0; j < i; ++j) < a[i][j] = 2; // Сначала пишем 2 ниже диагонали >a[i][j] = 1; // После завершения предыдущего цикла i==j, пишем 1 for (++j; j < n; ++j) // Цикл начинаем с увеличения j на 1 < a[i][j] = 0; // Записываем 0 выше диагонали >>

Многомерные массивы

Если необходимо хранить в массиве величину, зависящую от трёх параметров-индексов, например, a[i][j][k] , то для представления такого массива нужно использовать вектор, элементами которого будут двумерные векторы, то есть

vector > > a;

Их можно рассматривать как “трёхмерные” таблицы, но проще думать о таких массивах, просто как о величине, определяемой тремя параметрами-индексами.

Их тоже можно создавать в одну строку, добавив ещё один уровень вложенности конструкторов. Например, пусть требуется создать массив размера x×y×z, то есть первый индекс будет принимать значения от 0 до x-1, второй индекс от 0 до y-1, третий индекс от 0 до z-1. Можно использовать такое объявление:

vector > > a(x, vector >(y, vector(z)));

Передача двумерных массивов в функцию

Передавать двумерные массивы в функцию всегда лучше по ссылке, т.е. добавляя знак “&” перед идентификатором параметра. Например:

void print(vector > & a)

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

Форматирование чисел при выводе

Допустим, мы заполним массив таблицей умножения: a[i][j] = i * j как в примере в начале раздела. Если мы теперь попробуем вывести этот массив на экран, разделяя элементы в строке одним пробелом, то из-за того, что числа имеют различную длину столбцы таблицы окажутся неровными:

0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 0 2 4 6 8 10 12 14 16 18 0 3 6 9 12 15 18 21 24 27

Для того, чтобы получить ровные столбцы необходимо, выводить числа так, чтобы одно выводимое число имело ширину, например, ровно в 3 символа, а “лишние” позиции были бы заполнены пробелами. Тогда получится следующая таблица:

0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 0 2 4 6 8 10 12 14 16 18 0 3 6 9 12 15 18 21 24 27

Для того, чтобы выводимое число или строка имело ровно заданную ширину, необходимо перед выводом его на экран для потока cout вызвать метод width с параметром 3 . Данный метод устанавливает ширину поля для выводимого значения. Получим следующую программу для вывода:

for(int i = 0; i < n; ++i) < for(int j = 0; j < m; ++j) < cout.width(3); cout cout

Заметим, что мы теперь не выводим пробел после каждого числа, поскольку мы добавили этот пробел к ширине выводимого поля. Функция width действует однократно, только на следующее выводимый в поток значение, поэтому ее нужно вызывать перед каждым выводом числа на экран.

Внимание! Если выводимое число или строка имеет большую длину, чем это было установлено функцией width , то это число или строка будут выведены полностью, а не будет обрезано до указанного значения. То есть предпочтительней вывести результат некрасиво, нежели неверно.

Язык C++

Давайте напишем функцию add_item , которая принимает контейнер vector и добавляет в конец контейнера новый элемент. Мы могли бы начать со следующего кода:

#include #include #include using namespace std; // Здесь есть проблема void add_item(vectorstring> vec)  vec.push_back("New item!"); > int main()  vectorstring> vec; add_item(vec); for (string s : vec)  cout  <s  <'\n'; > return 0; > 

Если мы скомпилируем и запустим эту программу, то обнаружим, что после вызова функции add_item контейнер vec остался пустым. Проблема в том, что мы передали в функцию копию объекта vec . Внутри функции к этой копии был добавлен новый элемент, а после выхода из функции копия была удалена.

Следующий вариант нашей программы уже будет делать то что мы хотим:

// Здесь есть проблема vectorstring> add_item(vectorstring> vec)  vec.push_back("New item!"); return vec; > int main()  vectorstring> vec; vectorstring> vec2 = add_item(vec); for (string s : vec2)  cout  <s  <'\n'; > return 0; > 

Такая реализация, однако, является очень плохой идеей. Мы всего лишь хотели добавить один элемент в вектор, а вместо этого получили копию вектора с добавленным новым элементом. Помимо неверной логики работы, мы получили потенциальную проблему с производительностью: вместо константного времени мы тратим линейное время на добавление элемента в вектор.

Правильное решение нашей задачи в C++ выглядит следующим образом:

void add_item(vectorstring>& vec)  vec.push_back("New item!"); > int main()  vectorstring> vec; add_item(vec); for (string s : vec)  cout  <s  <'\n'; > return 0; > 

Символ амперсанд & позволяет передать в функцию ссылку на параметр. Работа с параметром внутри функции не изменяется, но вместо копии мы имеем дело именно с тем объектом, который был передан в функцию. Таким образом, мы избавились от лишнего копирования и реализовали правильную логику работы программы.

Рассмотрим другой пример. Допустим, мы хотим передать вектор в функцию, которая будет анализировать элементы вектора, но не будет его изменять. Например:

// Здесь есть проблема int count_greetings(vectorstring>& vec)  int counter = 0; for (string s : vec)  if (s == "Hello")  ++counter; > > return counter; > 

Мы уже достаточно грамотные и сразу передали вектор по ссылке, чтобы избежать ненужного копирования. Однако в текущем виде функция count_greetings имеет другую, более тонкую, проблему. Если мы нарушим договоренность и изменим вектор внутри функции count_greetings , то компилятор не увидит в этом проблемы. Проблему будем искать мы, когда поймем, что в каком-то месте нашей программы происходит неправильная манипуляция с вектором.

Хорошим стилем в данном случае является передача параметра по константной ссылке:

int count_greetings(const vectorstring>& vec)  int counter = 0; for (const string& s : vec)  if (s == "Hello")  ++counter; > > return counter; > 

Теперь компилятор не позволит изменить объект vec внутри функции count_greetings . Кроме того, теперь в коде явно выражена мысль о том, что объект передается в функцию только для чтения. Такой код проще читать и понимать логику его работы. Обратите внимание, что мы воспользовались константной ссылкой при определении переменной в цикле for . Здесь мы имеем дело с аналогичной ситуацией: в предыдущей версии в переменную s по очереди копировался каждый элемент вектора. Теперь же мы перебираем в цикле константные ссылки на объекты, не копируя их.

Иногда необходимо изменять элементы вектора в цикле. В таком случае необходимо использовать неконстантную ссылку:

for (string& s : vec)  s.push_back('!'); > 

Передача константной ссылки на объект в функцию, которая не имеет право изменять объект, имеет смысл только в том случае, если копирование объекта является дорогой операцией. В частности, нет никакого смысла в передаче по ссылке объектов int или double . Это наоборот может привести к потере производительности. Если же мы имеем дело со сложным объектом, таким как string или любым контейнером, то передача по константой ссылке является единственным верным решением.

Использование ссылок в C++ не ограничивается передачей параметров в функции, но с этого примера проще всего начать знакомство со ссылками. Ключевое слово const также имеет разнообразные применения в C++. О некоторых из них мы поговорим в дальнейшем.

Резюме

Мы обсудили три способа передачи параметров в функцию:

  • передача копии
  • передача по ссылке
  • передача по константной ссылке

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

Источники

  • isocpp.org/wiki/faq/references
  • Введение
  • Настройка рабочей среды
  • Язык C++
    • Работа с потоками ввода-вывода
    • Строки
    • Контейнеры стандартной библиотеки C++
    • Эффективная передача параметров в функцию
    • Алгоритмы стандартной библиотеки C++
    • Итераторы
    • Библиотеки numeric и random
    • Классы
    • Наследование
    • Динамическое выделение памяти
    • Обобщенное программирование

    Как передать двумерный вектор в функцию c

    Профиль
    Группа: Участник
    Сообщений: 153
    Регистрация: 11.8.2011

    Репутация: нет
    Всего: нет

    Добрый день.
    Подскажите пожалуйста, как передать двумерный вектор в поток в качестве параметра?

    Дата 17.8.2012, 06:47 (ссылка) | (голосов:1) Загрузка .

    Профиль
    Группа: Завсегдатай
    Сообщений: 4875
    Регистрация: 6.2.2010
    Где: Ростов-на-Дону

    Репутация: 6
    Всего: 135

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

    Дата 17.8.2012, 07:52 (ссылка) | (голосов:1) Загрузка .

    Профиль
    Группа: Комодератор
    Сообщений: 2706
    Регистрация: 9.8.2005
    Где: Тюмень

    Репутация: 99
    Всего: 106

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

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

    "Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."

    Дата 17.8.2012, 11:07 (ссылка) | (нет голосов) Загрузка .

    Профиль
    Группа: Участник
    Сообщений: 153
    Регистрация: 11.8.2011

    Репутация: нет
    Всего: нет

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

    Код
    void DdeData::InsertData(std::vector> Tbl, std::string strHsz1, short r, short c, short Rows, short Cols)
    if(strHsz1=="Stakan")
    _beginthreadex(NULL, 0, StakanThread, &Tbl, 0, NULL);
    >
    else if( и тд)
    .
    >
    >

    а вот функция потока

    Код
    unsigned _stdcall StakanThread(void* param) // поток для сохранения стакана
    dd.Stakan = (std::vector>*)param;
    >

    компилятор ругается на выделенную строку:

    Цитата
    Error 6 error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::vector *' (or there is no acceptable conversion) c:\users\андрей\documents\visual studio 2010\projects\r4t_2\r4t\ddedatarealization.cpp 130 1 R4T

    Это сообщение отредактировал(а) wallstreet - 17.8.2012, 11:09

    Передача указателя на динамический двумерный массив в функцию в C++

    Как передать указатель на динамический двумерный массив в функцию в C++?

    puding ☆
    20.08.13 19:49:22 MSK
    ← 1 2 3 4 →
    Ответ на: комментарий от comp00 21.08.13 01:14:28 MSK

    Чтобы быстрее понять указатели, не нужны никакие Шилдты, а тем более Страуструпы, которые еще больше запутают человека. Как вариант мне в свое время помогло осиливания того же x86 ассемблера, после чего сразу все стало понятно и больше вопросов не возникало.

    grouzen ★★
    ( 21.08.13 01:27:16 MSK )
    Ответ на: комментарий от cdshines 21.08.13 01:20:50 MSK

    Почитал K&R, кое-что из «Глава 5. Указатели и массивы». Понял мало, но по своей проблеме узнал, что для того, чтобы передавать двумерный массив в функцию без указания размерности нужно передавать указатель на указатель. Завтра буду курить.

    puding ☆
    ( 21.08.13 01:38:34 MSK ) автор топика
    Ответ на: комментарий от yoghurt 20.08.13 22:04:50 MSK

    Еще раз - где косвенность в твоих примерах?

    tailgunner ★★★★★
    ( 21.08.13 08:18:18 MSK )

    Еще один платиновый тред. Одни неучи пытаются рассказать другим неучам то, чего сами не знают. dev во всём великолепии.

    Чтобы быстрее понять указатели, не нужны никакие Шилдты, а тем более Страуструпы, которые еще больше запутают человека. Как вариант мне в свое время помогло осиливания того же x86 ассемблера, после чего сразу все стало понятно и больше вопросов не возникало.

    Как же хорошо, что я знакомство с компьютером именно с ассемблера и начинал. (А если точнее, со схемотехники ЭВМ и принципов работы процессора.)

    Все, кто пытается программировать, не зная, во что реально превращаются все объектно-ориентированные быдлокоды — поетнциальные обезьяны с гранатами. Никогда не знаешь, в какой момент вылезет их невежество и во что оно выльется.

    anonymous
    ( 21.08.13 08:49:03 MSK )
    Ответ на: комментарий от tailgunner 21.08.13 08:18:18 MSK

    Если ты говоришь о косвенности как о двойном (в общем случае - n-кратном) разыменовании указателя на указатель ([на указатель]*), то о какой косвенности может идти речь в случае

    int arr[3][3] = , , >; 

    , о котором говорю я?

    Очевидно оно разворачивается в что-то типа

    movl $1, -48(%rbp) movl $2, -44(%rbp) movl $3, -40(%rbp) movl $4, -36(%rbp) movl $5, -32(%rbp) movl $6, -28(%rbp) movl $7, -24(%rbp) movl $8, -20(%rbp) movl $9, -16(%rbp) 

    т.о. доступ к элементу осуществляется именно по формуле из примера.

    yoghurt ★★★★★
    ( 21.08.13 09:06:49 MSK )
    Ответ на: комментарий от yoghurt 21.08.13 09:06:49 MSK

    А, ты уже об этом случае говоришь. а ведь совсем недавно было:

    yoghurt> Разве доступ к элементу i,j в Т*[] не работает как *(base + i*rows + j)

    tailgunner ★★★★★
    ( 21.08.13 09:09:40 MSK )
    Ответ на: комментарий от yoghurt 21.08.13 09:06:49 MSK

    Между T[] и T[CONST] есть существенная разница.

    anonymous
    ( 21.08.13 09:12:19 MSK )
    Ответ на: комментарий от anonymous 21.08.13 09:12:19 MSK

    Между T[] и T[CONST] есть существенная разница.

    Да, ещё раз посыпая голову пеплом говорю, что неправильно назвал тип, о котором шла речь изначально.

    yoghurt ★★★★★
    ( 21.08.13 09:22:13 MSK )
    one_more_hokum ★★★
    ( 21.08.13 09:51:07 MSK )
    Ответ на: комментарий от comp00 21.08.13 00:43:48 MSK

    Ну смысл в том, что многомерные массивы, хранятся не в виде абстрактного тела (таблица/куб etc) как удобно изображать человеку, а в виде указателей на указатели.

    Двумерный массів: int[10][20] - честный сплошной участок памяті

    То что вы описали - это массив одномерных массивов.

    dzidzitop ★★
    ( 21.08.13 10:39:32 MSK )

    хоспади, куда смотрит наркоконтроль

    anonymous
    ( 21.08.13 10:55:03 MSK )
    Ответ на: хоспади, куда смотрит наркоконтроль от anonymous 21.08.13 10:55:03 MSK

    Re: хоспади, куда смотрит наркоконтроль

    Это не двумерный массив. Это вектор векторов.

    anonymous
    ( 21.08.13 10:57:20 MSK )
    Ответ на: комментарий от cdshines 21.08.13 01:24:32 MSK

    На этой неделе вообще по плюсам столько вопросов от тех, кто не читал k&r. Как будто k&r - многотомное собрание сочинений кернигана и риччи в 1200 страниц каждое, и всего 24 тома.

    nanoolinux ★★★★
    ( 21.08.13 11:52:48 MSK )
    Ответ на: комментарий от yoghurt 21.08.13 00:51:41 MSK

    Дружище, длинные и непонятные слова типа шаблоны и семантика меня угнетают, начинаю боятся выходить на улицу.

    #include #include typedef int MYCLASS; #define WIDTH 3 #define HEIGHT 2 MYCLASS a[WIDTH][HEIGHT]; typedef MYCLASS A[WIDTH][HEIGHT]; void print(A*a , int x,int y) < for(int i=0;i> int main(void) < for(int i=0;iA * a2=(A*)new int(WIDTH*HEIGHT); for(int i=0;i print(&a,WIDTH,HEIGHT); printf("\n"); print(a2,WIDTH,HEIGHT); return 0; > 

    ilovewindows ★★★★★
    ( 21.08.13 12:11:47 MSK )

     //Задать размерность int QwMatrixHeight = 20; int QwMatrixWidth = 20; //Создать матрицу QwMatrixElement*** QwMatrix; 
     //Выделить память под элементы матрицы for (int i = 0; i < QwMatrixHeight; i++) < for (int j = 0; j < QwMatrixWidth; j++) < QwMatrix[i][j] = new QwMatrixElement(); >> 
     //Передача матрицы в функцию void QwDoSomethingWithMatrix(QwMatrixElement*** matrix, int rows, int cols) < for (int i = 0; i < rows; i++) < for (int j = 0; j < cols; j++) < matrix[i][j]->someProperty = true; > > > 
     //Использование в программе QwDoSomethingWithMatrix(QwMatrix, QwMatrixHeight, QwMatrixWidth); 

    puding ☆
    ( 21.08.13 12:30:34 MSK ) автор топика
    Ответ на: комментарий от puding 21.08.13 12:30:34 MSK

    Это, как правильно сказали выше, массив массивов, а не многомерный массив.

    tailgunner ★★★★★
    ( 21.08.13 12:36:29 MSK )
    Ответ на: комментарий от tailgunner 21.08.13 12:36:29 MSK

    Это, как правильно сказали выше, массив массивов, а не многомерный массив.

    В его коде еще и выделение памяти под массивы отсутствует.

    anonymous
    ( 21.08.13 12:50:07 MSK )
    Ответ на: комментарий от anonymous 21.08.13 12:50:07 MSK

    Точно, чего то вывыливается в SIGSEGV. Что дописать?

    puding ☆
    ( 21.08.13 12:58:03 MSK ) автор топика
    Ответ на: комментарий от puding 21.08.13 12:30:34 MSK

    template void QwDoSomethingWithMatrix(T *matrix, int rows, int cols) < for (int i = 0; i < rows-1; i++) < for (int j = 0; j < cols-1; j++) < matrix[i][j]->someProperty = true; > > > 

    nanoolinux ★★★★
    ( 21.08.13 13:15:16 MSK )
    Последнее исправление: nanoolinux 21.08.13 13:16:02 MSK (всего исправлений: 1)

    Ответ на: комментарий от puding 21.08.13 12:58:03 MSK
    anonymous
    ( 21.08.13 14:02:50 MSK )
    Ответ на: комментарий от puding 21.08.13 12:30:34 MSK

    #include #include template using Matrix = std::array, h>; struct Something < bool property; int value; Something() : property(true) <>>; template void doSomething(Matrix& mx) < int x = 0; for (auto& col : mx) for (auto& el : col) < printf("%p\n", &el); el.value = ++x; >> int main() < printf("sizeof cell = %ld\n", sizeof(Something)); constexpr size_t h = 3, w = 3; Matrixmx; // movb $1, (%rsp) // movb $1, 8(%rsp) // movb $1, 16(%rsp) // movb $1, 24(%rsp) // movb $1, 32(%rsp) // movb $1, 40(%rsp) // movb $1, 48(%rsp) // movb $1, 56(%rsp) // movb $1, 64(%rsp) doSomething(mx); for (size_t i = 0; i < h; ++i) < printf("< "); for (size_t j = 0; j < w; ++j) printf("(%p)", &mx[i][j], mx[i][j].property, mx[i][j].value); puts(">"); > > /* sizeof cell = 8 0x7ff0000d8 0x7ff0000e0 0x7ff0000e8 0x7ff0000f0 0x7ff0000f8 0x7ff000100 0x7ff000108 0x7ff000110 0x7ff000118 < (0x7ff0000d8)(0x7ff0000e0) (0x7ff0000e8) > < (0x7ff0000f0)(0x7ff0000f8) (0x7ff000100) > < (0x7ff000108)(0x7ff000110) (0x7ff000118) > */ /* total heap usage: 0 allocs, 0 frees, 0 bytes allocated */ 

    Обрати внимание на 0 аллокаций в куче, на код аллокации на стеке, на дефолтный конструктор, на два способа обхода и на непрерывный layout элементов матрицы в памяти.

    Если h и w неизвестны на момент компиляции, то нужно делать как предложил tailgunner, то есть писать свой класс с аллокацией одним куском и доступом с помощью арифметики i * w + j индексов. На самом деле (по стандарту), для статических T[h][w] и std::array, h> компилятор сам располагает их непрерывно в памяти и пишет ту же самую арифметику для индексов в ассемблер для [ i ][j].

    quasimoto ★★★★
    ( 21.08.13 14:03:29 MSK )
    Последнее исправление: quasimoto 21.08.13 14:05:29 MSK (всего исправлений: 1)

    trex6 ★★★★★
    ( 21.08.13 14:45:23 MSK )
    Ответ на: комментарий от quasimoto 21.08.13 14:03:29 MSK

    это может приводить к code bloating если юзаются много інстансов с разнымі размерностями.

    anonymous
    ( 21.08.13 15:06:13 MSK )
    Ответ на: комментарий от trex6 21.08.13 14:45:23 MSK

    std::vector> xs = , , , <>>; for (auto const& col : xs) < for (auto const& el : col) printf("(%p)%d ", &el, el); puts(""); >/* (0x59f91d0)1 (0x59f91d4)2 (0x59f91d8)3 (0x59f9220)1 (0x59f9224)2 (0x59f9270)1 */ /* total heap usage: 7 allocs, 7 frees, 144 bytes allocated */ 

    144/4 = 36, куда ему столько? 🙂

    int xs[][3] = , , , <>>; for (auto const& col : xs) < for (auto const& el : col) printf("(%p)%d ", &el, el); puts(""); >/* (0x7ff0000e0)1 (0x7ff0000e4)2 (0x7ff0000e8)3 (0x7ff0000ec)1 (0x7ff0000f0)2 (0x7ff0000f4)0 (0x7ff0000f8)1 (0x7ff0000fc)0 (0x7ff000100)0 (0x7ff000104)0 (0x7ff000108)0 (0x7ff00010c)0 */ /* total heap usage: 0 allocs, 0 frees, 0 bytes allocated */ 

    (0x7ff00010c + 4 - 0x7ff0000e0) / 4 = 12 = 4 * 3.

    quasimoto ★★★★
    ( 21.08.13 15:15:55 MSK )
    Последнее исправление: quasimoto 21.08.13 15:23:46 MSK (всего исправлений: 1)

    Ответ на: комментарий от anonymous 21.08.13 15:06:13 MSK

    это может приводить к code bloating если юзаются много інстансов с разнымі размерностями

    Если много разных h и w, то тоже — класс с size_t (const если нет resize -> realloc) h, w; T *data; и i * w + j в operator().

    quasimoto ★★★★
    ( 21.08.13 15:20:32 MSK )
    Последнее исправление: quasimoto 21.08.13 15:26:34 MSK (всего исправлений: 1)

    Ответ на: комментарий от quasimoto 21.08.13 15:15:55 MSK

    А если размерности нормальные взять? 500х500?

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

    trex6 ★★★★★
    ( 21.08.13 15:36:10 MSK )
    Ответ на: комментарий от dzidzitop 21.08.13 10:39:32 MSK

    многомерные массивы в памяти представляются именно как массивы одномерных массивов и никак иначе.

    comp00 ★★★★
    ( 21.08.13 15:48:27 MSK )
    Ответ на: комментарий от puding 21.08.13 12:58:03 MSK

    Упрямый ты. Ну как хочешь : ССЗБ. Подсказываю последний раз: man new, delete

    comp00 ★★★★
    ( 21.08.13 15:50:43 MSK )
    Последнее исправление: comp00 21.08.13 15:50:53 MSK (всего исправлений: 1)

    Ответ на: комментарий от comp00 21.08.13 15:48:27 MSK

    рекомендую проверить. Сейчас под рукой GCC нет. Можно создать массив 2 на 2 и итерацией + печатью значений указателей на элементы проверіть.

    anonymous
    ( 21.08.13 16:01:48 MSK )
    Ответ на: комментарий от anonymous 21.08.13 16:01:48 MSK

     1 #include 2 #define SIZE 4 3 int main(int argc, char **argv) < 4 int **array = new int*[SIZE]; 5 for(int i=0;i8 9 for(int i=0; i 13 std::cout 15 for(int i=0;i 18 delete [] array; 19 return 0; 20 > 
    ./tst 0x1989040 0x1989044 0x1989048 0x198904c 0x1989060 0x1989064 0x1989068 0x198906c 0x1989080 0x1989084 0x1989088 0x198908c 0x19890a0 0x19890a4 0x19890a8 0x19890ac 

    Все правильно.

    comp00 ★★★★
    ( 21.08.13 16:15:47 MSK )
    Последнее исправление: comp00 21.08.13 16:16:00 MSK (всего исправлений: 1)

    Ответ на: комментарий от anonymous 21.08.13 16:01:48 MSK

    Я поясню: понятно, что память под вектор выделяются последовательно. Но совершенно не факт, что вектор буде идти один за другим.

    comp00 ★★★★
    ( 21.08.13 16:21:59 MSK )
    Ответ на: комментарий от comp00 21.08.13 16:15:47 MSK

    это не многомерный массів.

    Многомерный массів: int array[2][2] = , >;

    anonymous
    ( 21.08.13 16:34:36 MSK )
    Ответ на: комментарий от anonymous 21.08.13 16:34:36 MSK

    Это (у меня) многомерный массив. А ты путаешь кучу со стеком

    comp00 ★★★★
    ( 21.08.13 16:40:40 MSK )
    Ответ на: комментарий от comp00 21.08.13 16:40:40 MSK
    anonymous
    ( 21.08.13 16:42:33 MSK )
    Ответ на: комментарий от anonymous 21.08.13 16:42:33 MSK

    тупняк.
    Массивы могут выделяться на стеке или на куче
    естественно, что это совершенно разные структуры, и способы организации памяти у них разные.
    Тот факт что в википедии под array подразумевают выделение на стеке, не означает что массивы, выделенные на куче не являются массивами.
    Да по факту это не многомерный массив, в массив указателей. Но знаешь ли, компьютер тоже не с 0 и 1 работает, как в школе преподают.

    comp00 ★★★★
    ( 21.08.13 16:59:06 MSK )
    Последнее исправление: comp00 21.08.13 17:00:50 MSK (всего исправлений: 1)

    Ответ на: комментарий от anonymous 21.08.13 08:49:03 MSK

    однако понимание что ключ-значение бд есть включающая в себя указатели общность облегчает понимание указателей и вообще различение/общее среди имя-именуемое

    qulinxao ★★☆
    ( 21.08.13 17:10:54 MSK )
    Ответ на: комментарий от trex6 21.08.13 15:36:10 MSK

    А если размерности нормальные взять?

    А 3x3 или 4x4 это не нормальные?

    Чтобы стек сорвать это мало, наверно. Нужно побольше.

    #include #include template class Matrix < T *data; public: typedef T data_type; const size_t rows, cols; Matrix(const size_t rows, const size_t cols) : data(new T[rows * cols]), rows(rows), cols(cols) <>~Matrix() < delete[] data; >T& operator()(const size_t i, const size_t j) const < return data[i * cols + j]; >>; int main() < #ifdef VECTOR std::vector> mx(ROWS); for (size_t i = 0; i < mx.size(); ++i) < mx[i].resize(COLS); for (size_t j = 0; j < mx[i].size(); ++j) mx[i][j] = i * j; >long long sum = 0; for (size_t i = 0; i < mx.size(); ++i) for (size_t j = 0; j < mx[i].size(); ++j) sum += mx[i][j]; printf("%lld\n", sum); #else Matrixmx(ROWS, COLS); for (size_t i = 0; i
    $ ~ g++ -DVECTOR -DROWS=15000 -DCOLS=20000 -O1 mx.cc $ ~ \time ./a.out 22497375075000000 1.01user 0.60system 0:01.69elapsed 95%CPU (0avgtext+0avgdata 4692848maxresident)k 0inputs+0outputs (0major+293433minor)pagefaults 0swaps $ ~ g++ -DROWS=15000 -DCOLS=20000 -O1 mx.cc $ ~ \time ./a.out 22497375075000000 0.59user 0.85system 0:01.48elapsed 97%CPU (0avgtext+0avgdata 4691488maxresident)k 128inputs+0outputs (1major+3035minor)pagefaults 0swaps $ ~ g++ -DVECTOR -DROWS=15000 -DCOLS=20000 -O2 mx.cc $ ~ \time ./a.out 22497375075000000 0.75user 0.56system 0:01.38elapsed 94%CPU (0avgtext+0avgdata 4692864maxresident)k 0inputs+0outputs (0major+293435minor)pagefaults 0swaps $ ~ g++ -DROWS=15000 -DCOLS=20000 -O2 mx.cc $ ~ \time ./a.out 22497375075000000 0.44user 0.33system 0:00.79elapsed 97%CPU (0avgtext+0avgdata 4690672maxresident)k 0inputs+0outputs (0major+1503minor)pagefaults 0swaps $ ~ g++ -DVECTOR -DROWS=15000 -DCOLS=20000 -Ofast mx.cc $ ~ \time ./a.out 22497375075000000 0.47user 0.60system 0:01.15elapsed 93%CPU (0avgtext+0avgdata 4692864maxresident)k 0inputs+0outputs (0major+293435minor)pagefaults 0swaps $ ~ g++ -DROWS=15000 -DCOLS=20000 -Ofast mx.cc $ ~ \time ./a.out 22497375075000000 0.40user 0.34system 0:00.76elapsed 98%CPU (0avgtext+0avgdata 4691456maxresident)k 0inputs+0outputs (0major+992minor)pagefaults 0swaps 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *