Можем ли мы создать статический массив с размером, который является постоянной времени выполнения? - PullRequest
6 голосов
/ 22 октября 2010

Мы все знаем основные правила для статического массива:

int size = 20;
char myArray[size];

не законно. И.

const int size = 20;
char myArray[size];

в порядке.

Но как насчет этого?

int f(const int size)
{
    char myArr[size];
}

void main()
{
   f(2);
   f(1024);
}

MSVC говорит, что это ошибка, gcc, похоже, компилирует и выполняет ее нормально.

Очевидно, что он не переносимый, но должен ли он быть принят?

Какой компилятор делает правильные вещи в этой ситуации?

Кроме того, если это разрешено компилятором, должно ли оно быть разрешено хорошими стандартами / практикой программирования?

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

Ответы [ 5 ]

9 голосов
/ 22 октября 2010

Нет. C ++ не имеет массивов переменной длины. C99 делает, и GCC позволяет это через расширение.

Используйте std::vector.


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

Распределение стека происходит очень быстро, но, вероятно, это не будет главной проблемой в реальном приложении. (У вас должна быть собственная схема управления памятью, которая будет приближаться по скорости к выделению стека.)

3 голосов
/ 22 октября 2010

Для этого вы можете использовать std :: array. std :: array был добавлен в расширениях TR1 и доступен в пространстве имен std или std :: tr1 в зависимости от используемой версии компилятора / стандартной библиотеки.

#incldue <array>
int main()
{
   std::tr1::array<int,25> myArray;
   //etc...
   myArray[2] = 42;
}

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

Если вы хотите разместить в стеке, вы можете использовать alloca для размещения в стеке вместо malloc (применяются все обычные предупреждения).

Если вы хотите использовать более дружественный интерфейс, вы можете реализовать собственный распределитель stl на основе alloca и использовать std :: vector с этим ( вы должны прочитать до реализации ).

например:.

#include <vector>
template <class T>
class MyStackAllocator
{ // implemented per std::allocator spec
...
}
int main()
{
//allocate a vector on the stack and reserve n items
vector<int, MyStackAllocator<T>> vecOnStack(25);
...
}
1 голос
/ 29 октября 2010

Правильный ответ был фактически предоставлен в этой теме, поэтому я просто хочу предоставить ему больше контекста.

Вам необходимо создать собственный распределитель, который использует функцию alloca() (или _alloca() в Windows) для динамического выделения стека. Его очень легко создать, вы можете использовать типичный шаблон распределителя, изменить функцию-член allocate () на return (pointer)(alloca(size * sizeof(T))); и сделать функцию deallocate() пустой, потому что нет ручного освобождения стека. После этого вы можете поставить свой распределитель в стандартные контейнеры, например, vector<T, stack_allocator<T>>.

Однако есть две оговорки. Размер стека, выделяемого для выделения, может значительно различаться, часто у вас есть возможность установить его во время компиляции. Visual Studio в 32-битных приложениях, по-моему, ограничивает его размером до 1 МБ. Другие компиляторы могут иметь другие ограничения. В 64-битных приложениях проблем на самом деле нет, так как стек может быть размером с кучу. Вероятно, вам потребуется перехватить структурированное исключение при переполнении стека и преобразовать его в исключение C ++.

Второе предупреждение: вам никогда не следует копировать указатели стека за пределы вашей функции, поэтому семантика перемещения, например, не будет работать, если вы передадите объекты, выделенные из стека, в / из функции.

И еще одно неудобство: вы не можете копировать контейнеры с несовместимыми распределителями, но вы можете копировать их поэлементно. Э.Г.

vector<int> vint;
vector<int, static_allocator<int>> vsint;

vint = vsint; // won't compile, different allocators
std::copy(vsint.begin(), vsint.end(), vint.begin()); // fine
1 голос
/ 22 октября 2010

Массивы переменной длины (VLA) поддерживаются C99, но не C ++. GCC позволяет это через расширение.

std :: vector делает в C ++ то же, что делает VLA в C99

0 голосов
/ 22 октября 2010

То, что вы действительно хотите, не совсем то, что вы просили. Кажется, что вы действительно хотите сопоставить числа с числами, чтобы индекс 2042 содержал значение 23 и т. Д.

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

---- Ответ на вопрос, можем ли мы выделить статический массив с постоянной времени выполнения ниже ---

Если вам нужен статический массив времени выполнения, лучшим вариантом будет объявить массив с использованием альтернативного синтаксиса указателя, а затем инициализировать его в нестатической init() подобной функции в начале выполнения программы. *

Если вы попытаетесь выделить массив до выполнения main(...), вы рискуете не знать, какие статические блоки кода будут вызываться в каком порядке. Иногда это мало что меняет; но в вашем случае кажется, что вам нужно вычислить число во время выполнения, поэтому порядок модулей становится важным.

Используя нестатический метод, вы по существу гарантируете, что весь статический код будет обработан до выделения массива.

...