Это плохая практика, чтобы объявить массив в середине функции - PullRequest
2 голосов
/ 08 июня 2010

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

void MyFunction()
{

    int size;

    //do a bunch of stuff
    size = 10; //but could have been something else
    int array[size];

    //do more stuff...

}

Ответы [ 5 ]

2 голосов
/ 08 июня 2010

Обычно да, это плохая практика, хотя новые стандарты позволяют использовать этот синтаксис. По моему мнению, вы должны выделить (в куче) память, которую хотите использовать, и освободить ее, как только закончите с ней. Поскольку не существует переносимого способа проверки, достаточно ли стека для хранения этого массива, вы должны использовать некоторые методы, которые действительно можно проверить - например, malloc / calloc & free. Во встроенном мире размер стека может быть проблемой.

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

1 голос
/ 11 июня 2010

Аргумент против VLA заключается в том, что из-за абсолютной опасности переполнения стека к тому времени, когда вы достаточно продумывали / проверяли, чтобы сделать их безопасными, вы уже достаточно продумывали / проверяли, чтобы использовать массив фиксированного размера :

1) Чтобы безопасно использовать VLA, вы должны знать, что достаточно стека.

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

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

4) Если вы когда-нибудь столкнетесь с одним из оставшихся случаев, в ситуации, когда выполнение malloc недопустимо, сообщите мне ...

Может быть более удобным, из POV исходного кода, использовать VLA. Например, вы можете использовать sizeof (в определяющей области действия) вместо сохранения размера в переменной, и для того, чтобы разделить массив на куски, может потребоваться передача дополнительного параметра. Так что иногда есть небольшой выигрыш в удобстве.

Также проще не заметить, что вы используете огромное количество стеков, что приводит к неопределенному поведению, если вместо довольно страшного int buf[1920*1024] или int buf[MAX_IMG_SIZE] у вас есть int buf[img->size]. Это прекрасно работает вплоть до первого раза, когда вы на самом деле обрабатываете большое изображение. В общем, это проблема правильного тестирования, но если вы пропустите некоторые возможные трудные входные данные, то это будет не первый или последний набор тестов, который это сделает. Я обнаружил, что массив с фиксированным размером напоминает мне либо о проверке входных данных с фиксированным размером, либо о замене его динамическим размещением и перестает беспокоиться о том, помещается ли он в стек или нет. Нет действительного варианта положить его в стек, и не беспокоиться, подходит ли он ...

1 голос
/ 08 июня 2010

Это зависит. Первое явно не то, что я бы назвал «правильным», а второе только в довольно ограниченных обстоятельствах.

Во-первых, вы не должны разыгрывать возврат из malloc в C - это может скрыть ошибку случайного исключения правильного заголовка (<stdlib.h>).

Во-вторых, вы ограничиваете код C99 или расширением gcc. Пока вы знаете об этом, и это работает для ваших целей, все в порядке, но вряд ли то, что я бы назвал идеалом переносимости.

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

1 голос
/ 08 июня 2010

По вашему вопросу, я думаю, что у каждого есть свои преимущества и недостатки.

Динамическое распределение:
Медленно, но вы можете определить, когда нет памяти для вашего программиста, проверив указатель.

Распределение стека:
Только в C99 и это невероятно быстро, но в случае переполнения стека вам не повезло.

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

0 голосов
/ 08 июня 2010

две точки с точки зрения UNIX / C -

malloc работает медленно только когда вы заставляете его вызывать brk (). Значение для разумных массивов это то же самое, что выделение стекового пространства для переменной. Кстати, когда вы используете метод # 2 (через alloca и в коде libc, который я видел), он также вызывает brk () для огромных объектов. Так что это стирка. Примечание: с # 2 & # 1 вам все равно придется прямо или косвенно вызывать вызов типа memset для обнуления байтов в массиве. Это просто примечание к реальной проблеме (ИМО):

Настоящая проблема - утечки памяти. alloca очищается после себя, когда функция перезапускается, так что # 2 с меньшей вероятностью вызовет проблему. С malloc / calloc вы должны вызвать free () или начать утечку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...