Нет: это не будет работать со стеком, как это обычно делается. Переменная в стеке занимает фиксированный диапазон адресов. Следующая переменная появляется сразу после нее, поэтому нет места для роста. Рассмотрим функцию, подобную этой:
void f(int x) {
int i;
float *a = alloca(40 * sizeof(float));
int k;
…
}
Стек после пролога функции выглядит примерно так:
----------------+-----+-----+-----+-------------------+-----+---------------------
... | ret | x | i | a | k | ...
----------------+-----+-----+-----+-------------------+-----+---------------------
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
previous frames f's frame free space at the top
Нет места для роста a
.
Я показываю очень упрощенный пример: в реальном мире переменные попадают в регистры, переменные можно переупорядочивать, даже если они попадают в стек и т. Д. Но только одна переменная может быть последней в стек с местом для роста.
Таким образом, если существует realloca
, его можно применить только к переменной, которая находится наверху стека. (В противном случае ему пришлось бы переместить все остальное, что находится поверх него, но для этого потребовалось бы обновить все существующие указатели на те, что вообще невозможно.) Это был бы очень ограниченный механизм, поэтому поддержка этой функции будет иметь очень маленькая выгода. Его поддержка будет иметь значительную стоимость, поскольку компиляторы обычно могут свободно размещать вещи в стеке в нужном им порядке: для этой функции потребуется новый механизм, позволяющий компилятору знать, что одна конкретная переменная должна идти наверх.
Возможно, что где-то реализация C где-то имеет realloca
, но это маловероятно, учитывая соотношение затрат и выгод.
Конечно, realloca
может быть легко реализовано, если alloca
не использует стратегию распределения стека. Но выделение в стеке - это весь смысл alloca
. Если вам нужны объекты с изменяемым размером, вам нужна структура управления памятью с интерфейсом кучи, и именно для этого malloc
.
На практике есть несколько возможных подходов к динамическому управлению памятью в библиотеке.
Наиболее распространенный подход - звонить malloc
, realloc
и free
, когда они вам нужны. Вот для чего они.
В некоторых средах полезно поддерживать пользовательские распределители. Вы можете предоставить пользователю библиотеки возможность передавать указатели на альтернативные реализации malloc
, realloc
и free
. Это полезно, когда вы хотите написать переносимую библиотеку, которая должна использоваться кодом, который сам по себе полностью переносим. Однако в большинстве случаев пользователи, которые хотят использовать собственные распределители, могут сделать это, связав своих malloc
и друзей. И даже , что редко бывает полезным.
Если вам нужен код, который может работать в среде без динамического выделения (например, в критических для безопасности средах), вам также не следует использовать alloca
. alloca
хуже, чем malloc
, потому что это вызывает непредсказуемое использование стека и может привести к переполнению стека, которое вообще не будет обнаружено, или которое будет обнаружено только при сбое программы. Если вам нужен переменный (или большой) объем временной памяти в функции, попросите пользователя передать вам буфер подходящего размера.
/** [documentation of the function] …
* working_buffer must point to an array of floats of 3*n elements.
*/
void f(size_t n, float *working_buffer);
Лучше, если у вас есть бюджет размера кода, передайте размер массива и проверьте его.
/** [documentation of the function] …
* working_buffer must point to an array of floats of 3*n elements.
*/
int f(size_t n, float *working_buffer, size_t working_buffer_length)
{
if (working_buffer_length < 3 * n) return -EINVAL;
…
}