Alloca вместо локальной переменной в Alsa

Я использовал пример программы C ALSA в качестве справочной и пробежал по следующему коду:

...
snd_ctl_event_t *event;
snd_ctl_event_alloca(&event);
...

Основываясь на исходном коде ALSA, snd_ctl_event_alloca это макрос, который вызывает __snd_alloca который является макросом, который, наконец, расширяется до следующей эквивалентной строки для snd_ctl_event_alloca(&event); (с некоторым простым упрощением):

event = (snd_ctl_event_t *) alloca(snd_ctl_event_sizeof());
memset(event, 0, snd_ctl_event_sizeof());

где snd_ctl_event_sizeof() реализуется только один раз во всей библиотеке как:

size_t snd_ctl_event_sizeof()
{
    return sizeof(snd_ctl_event_t);
}

Итак, мой вопрос, не является ли весь этот процесс эквивалентным простому выполнению:

snd_ctl_event_t event = {0};

Для справки, это макросы:

#define snd_ctl_event_alloca(ptr) __snd_alloca(ptr, snd_ctl_event)
#define __snd_alloca(ptr,type) do { *ptr = (type##_t *) alloca(type##_sizeof()); memset(*ptr, 0, type##_sizeof()); } while (0)

Разъяснения:

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

РЕДАКТИРОВАТЬ

Как выясняется (из того, что я понимаю), занимаюсь:

snd_ctl_event_t event;

дает storage size of 'event' isn't known ошибка, потому что snd_ctl_event_t по-видимому, непрозрачная структура, которая определяется в частном порядке. Поэтому единственным вариантом является динамическое распределение.

1 ответ

Решение

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

Одна существенная проблема с непрозрачными типами данных заключается в том, что в стандартном C вы по существу вынуждены динамически размещать их в непрозрачной функции библиотеки. Невозможно неявно объявить непрозрачный объект локально. Это отрицательно влияет на эффективность и часто вынуждает клиента осуществлять дополнительное управление ресурсами (т. Е. Не забывать освобождать объект, когда он больше не нужен). Выставление точного размера непрозрачного объекта (через функцию в данном случае) и опора на alloca Выделить хранилище как можно ближе к более эффективному и довольно беззаботному локальному объявлению.

Если срок службы всей функции не требуется, alloca можно заменить на VLA, но авторы, вероятно, не хотели / не могли использовать VLA. (Я бы сказал, что использование VLA еще больше приблизит эмуляцию к истинному локальному объявлению.)

Часто для реализации той же технологии непрозрачный размер объекта может быть представлен как константа времени компиляции в заголовочном файле. Однако использование функции имеет дополнительное преимущество, заключающееся в том, что нет необходимости перекомпилировать весь проект, если размер объекта в этой изолированной библиотеке изменяется (как @R. Отмечено в комментариях).


Предыдущая версия ответа (пункты ниже все еще применимы, но, видимо, вторичны):

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

В таких ситуациях, как

some_type *ptr;

if (some condition)
{
  ...
  ptr = /* alloca one object */;
  ...
}
else
{
  ...
  ptr = /* alloca another object */;
  ...
}

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

Другое несвязанное различие в семантике заключается в том, что memset обнулит все байты объекта, в то время как = { 0 } не гарантируется обнуление байтов заполнения (если есть). Это может быть важно, если объект затем используется с некоторыми двоичными API (например, отправляется в сжатый поток ввода-вывода).

Другие вопросы по тегам