2

Я немного новичок в программировании в целом, и у меня возникла проблема с объявлением 3D и 4D массивов. У меня есть несколько объявлений, как это в начале моей главной функции, но я сузил проблему вплоть до этих 4:Исключение переполнения стека при объявлении многомерных массивов

string reg_perm_mark_name[64][64][64]; 
short reg_perm_mark_node_idex[64][64][64]; 
short reg_perm_mark_rot[64][64][64][4]; 
short reg_perm_mark_trans[64][64][64][3]; 

Когда я запускаю свою программу, с этим, я получаю «System.StackOverflowException» в моем исполняемый файл. Я бы предпочел бы, чтобы их динамически выделять. То, как я это сейчас, должно было быть временным, и я не уверен, как правильно объявлять указатели массива.

4 элемента, которые я использую в четырехмерном массиве reg_perm_mark_trans, например, являются [индекс узла] [индекс региона] [индекс маркера] [координаты xyz]. Также имеется всего 35 многомерных массивов, объявленных сразу. (большинство из них - 1D и 2D). Я не уверен, что это помогает.

Может ли кто-нибудь показать мне, как сделать эти 4d-массивы, или, возможно, сделать их динамически выделяя указателями или векторами? Будь описательным, я все еще учусь.

+2

Пространство для таких «простых» переменных намеренно очень ограничено. Ваши массивы слишком большие. Используйте 'vector' и т. Д. (Который использует' new' внутри, это не ограничено.). Есть достаточно примеров, как использовать 'std :: vector' в Интернете. – deviantfan

+0

Стандартные контейнеры являются плохой заменой для многомерных массивов. – celticminstrel

+1

@celticminstrel 'std :: array' - отличная замена для голых многомерных массивов. То же самое относится к 'std :: vector', когда вы работаете с многомерными массивами с динамическим размером. –

ответ

4

Предполагая для простоты, что sizeof(string) == 2 (это, вероятно, больше), вы пытаетесь выделить (64^3) * 9 * 2 байта в стеке. Это составляет 4 718 592 байта, или примерно 4,5 миллиона. Скорее всего, у вас просто нет 4,5 MiB, доступных в вашем стеке.

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

  1. декларировать их static.

  2. Объявить их за пределами main(), как глобальные переменные.

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

Также может быть способ сообщить вашему компилятору, что программе требуется больше пространства для стека, но я думаю, что сделать их статичными, вероятно, лучшим решением здесь. Если бы они находились в функции, отличной от main(), вам, вероятно, понадобилось бы сделать что-то еще.

+0

Что-то очень неправильно в отношении вашего байтового расчета. Это больше похоже на МБ, а не на ГБ. И откуда это взялось? Btw. строка 'std :: string' не может использовать меньше, чем указатель (и на самом деле, в зависимости от реализации, это намного больше) – deviantfan

+0

... вы правы. Это MiB, а не GiB. И 9 получается из суммы его четвертых измерений (4 и 3) плюс тот факт, что два других являются трехмерными.Я только оцениваю 'sizeof (string) == 2', потому что он упростил вычисление, сделав все базовые типы одинакового размера. Это только оценка. – celticminstrel

+1

Существует множество контекстов, в которых вы не можете объявлять переменные 'static' или сделать их глобальными (например, многопоточными), и вам нужно будет динамически распределять. –

2

Эта линия:

string reg_perm_mark_name[64][64][64] 

объявляет 64 * 64 * 64 = 262144 строк в стеке. A std::string обычно составляет около 32 байт, что составляет около 8 МБ. Максимальный размер стека обычно составляет около 1 МБ.

Чтобы объявить массив динамически, вы можете использовать std::vector. Как правило, многомерные std::vector s может быть немного громоздким и часто лучше объявить одномерный вектор и преобразование в единый индекс при доступе к элементу:

std::vector<std::string> reg_perm_mark_name(64*64*64); 

int i = 13; 
int j = 27; 
int k = 7; 
reg_perm_mark_name[i + 64*j + 64*64*k] = "Hello world!"; 

Но в этом случае вы можете объявить мульти- мерного std::vector, используя std::array вместо std::vector для внутренних типов. Использование std::array позволяет избежать слишком большого объема выделения памяти, поскольку они имеют фиксированный размер.Я хотел бы использовать или определения типов, используя псевдонимы, чтобы сделать объявление более ясным:

using StrArray = std::array<std::string, 64>; 
using StrArray2D = std::array<StrArray, 64>; 

std::vector<StrArray2D> reg_perm_mark_name(64); 

reg_perm_mark_name[3][4][7] = "Hello world!"; 
+0

Совершенно неважно, подходят ли внутренние типы стека. Если внешний тип является вектором, все, кроме одного вектора (обычно 3 слова), будет в куче. На самом деле, если внешний тип самого типа является вектором, но все внутренние типы являются массивом, вы получаете лучшее из всего: одно выделение, почти нет накладных расходов, отсутствие возможности стека над потоком и хорошая нотация индексации. –

+1

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

+0

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

2

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

Обратите внимание, что если вы используете массивы C++, то использование и след такие же, но они ведут себя как соответствующие контейнеры:

array<array<array<string,64>,64>,64> reg_perm_mark_name; 

Для использования динамического распределения небезопасной, вы могли бы написать:

auto reg_perm_mark_rot = new short[64][64][64][4]; 
// ... 
delete[] reg_perm_mark_rot; 

Чтобы использовать его безопасно, поскольку C++ 14 (обратите внимание, что самое внутреннее измерение получает специальную обработку):

auto reg_perm_mark_rot = std::make_unique<short[][64][64][4]>(64); 

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

+0

Вы, кажется, прокомментировали неправильный пост. – celticminstrel

+1

Не существует дополнительного уровня косвенности между массивом c и std-массивом. Вы должны использовать std :: array. –

+0

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

Смежные вопросы