Alloca вместо локальной переменной в Alsa - PullRequest
0 голосов
/ 16 октября 2018

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

...
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)

Пояснения:

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

EDIT

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

snd_ctl_event_t event;

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

1 Ответ

0 голосов
/ 16 октября 2018

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

Одна существенная проблема с непрозрачными типами данных заключается в том, что в стандартном 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 (например, отправляется в сжатый поток ввода-вывода).

...