0

我使用示例 C ALSA 程序作为参考,并运行以下代码:

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

基于 ALSA 源代码,snd_ctl_event_alloca是一个宏,调用__snd_allocawhich 是一个宏,最终扩展为以下等效行snd_ctl_event_alloca(&event);(经过一些简单的简化):

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

wheresnd_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显然是一个私有定义的不透明结构。因此,唯一的选择是动态分配。

4

1 回答 1

3

由于它是一个不透明的结构,所有这些操作的目的显然是实现一个不透明的数据类型,同时节省所有“优点”并至少击败他们的一些“缺点”。

不透明数据类型的一个突出问题是,在标准 C 中,您基本上被迫在不透明库函数中动态分配它们。不可能在本地隐式声明不透明对象。这会对效率产生负面影响,并经常迫使客户端实施额外的资源管理(即记住在不再需要时释放对象)。公开不透明对象的确切大小(在这种情况下通过函数)并依赖于alloca分配存储,这与您可以获得更高效且相当轻松的本地声明一样接近。

如果不需要函数范围的生命周期,alloca可以用 VLA 替换,但作者可能不想/不能使用 VLA。(我会说使用 VLA 会更接近于模拟真正的本地声明。)

通常为了实现相同的技术,不透明对象的大小可能会作为编译时常量暴露在头文件中。但是,如果此隔离库中的对象大小发生变化(如注释中的@R. 所述),使用函数还有一个额外的好处,即不必重新编译整个项目。


以前版本的答案(以下几点仍然适用,但显然是次要的):

它不完全等价,因为它alloca违反了基于范围的生命周期规则。-ed 内存的生命周期alloca延伸到函数的末尾,而本地对象的生命周期仅延伸到块的末尾。这可能是一件坏事,也可能是一件好事,这取决于你如何使用它。

在像这样的情况下

some_type *ptr;

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

语义上的差异可能至关重要。无论是不是你的情况 - 我不能从你到目前为止发布的内容中说出来。

另一个不相关的语义差异是memset将对象的所有字节清零,而= { 0 }不能保证将填充字节(如果有)清零。如果该对象随后与一些基于二进制的 API 一起使用(例如发送到压缩的 I/O 流),这可能很重要。

于 2018-10-15T23:59:54.780 回答