alloca 而不是 alsa 中的局部变量
alloca instead of local variable in 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
显然是一个不透明的结构,它是私有定义的。因此唯一的选择是动态分配。
由于它是一个不透明的结构,所有这些操作的目的显然是实现一个不透明的数据类型,同时保存所有 "pros" 并至少击败其中的一些 "cons"。
不透明数据类型的一个突出问题是,在标准 C 中,您基本上被迫在不透明库函数中动态分配它们。不可能在本地隐式声明不透明对象。这会对效率产生负面影响,并且通常会迫使客户端实施额外的资源管理(即记住在不再需要时释放对象)。公开不透明对象的确切大小(在本例中通过一个函数)并依靠 alloca
来分配存储,这与您可以获得更高效且相当无忧无虑的本地声明一样接近。
如果不需要函数范围的生命周期,alloca
可以用 VLA 替换,但作者可能没有 want/couldn 不使用 VLA。 (我想说使用 VLA 会更接近于模拟真正的本地声明。)
通常为了实现相同的技术,不透明对象的大小可能会在头文件中作为编译时常量公开。但是,使用函数还有一个额外的好处,即如果这个独立库中的对象大小发生变化,则不必重新编译整个项目(如@R。评论中所述)。
上一版答案(以下几点仍然适用,但显然是次要的):
它并不完全等同,因为 alloca
违背了基于作用域的生命周期规则。 alloca
-ed 内存的生命周期延伸到函数的末尾,而本地对象的生命周期仅延伸到块的末尾。这可能是一件坏事,也可能是一件好事,这取决于你如何使用它。
在
这样的情况下
some_type *ptr;
if (some condition)
{
...
ptr = /* alloca one object */;
...
}
else
{
...
ptr = /* alloca another object */;
...
}
语义上的差异可能至关重要。不管是不是你的情况 - 从你目前发布的内容我不能说。
另一个语义上不相关的差异是 memset
会将对象的所有字节置零,而 = { 0 }
不保证将填充字节(如果有)置零。如果该对象随后与一些基于二进制的 API 一起使用(例如发送到压缩的 I/O 流),这可能很重要。
我使用示例 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
显然是一个不透明的结构,它是私有定义的。因此唯一的选择是动态分配。
由于它是一个不透明的结构,所有这些操作的目的显然是实现一个不透明的数据类型,同时保存所有 "pros" 并至少击败其中的一些 "cons"。
不透明数据类型的一个突出问题是,在标准 C 中,您基本上被迫在不透明库函数中动态分配它们。不可能在本地隐式声明不透明对象。这会对效率产生负面影响,并且通常会迫使客户端实施额外的资源管理(即记住在不再需要时释放对象)。公开不透明对象的确切大小(在本例中通过一个函数)并依靠 alloca
来分配存储,这与您可以获得更高效且相当无忧无虑的本地声明一样接近。
如果不需要函数范围的生命周期,alloca
可以用 VLA 替换,但作者可能没有 want/couldn 不使用 VLA。 (我想说使用 VLA 会更接近于模拟真正的本地声明。)
通常为了实现相同的技术,不透明对象的大小可能会在头文件中作为编译时常量公开。但是,使用函数还有一个额外的好处,即如果这个独立库中的对象大小发生变化,则不必重新编译整个项目(如@R。评论中所述)。
上一版答案(以下几点仍然适用,但显然是次要的):
它并不完全等同,因为 alloca
违背了基于作用域的生命周期规则。 alloca
-ed 内存的生命周期延伸到函数的末尾,而本地对象的生命周期仅延伸到块的末尾。这可能是一件坏事,也可能是一件好事,这取决于你如何使用它。
在
这样的情况下some_type *ptr;
if (some condition)
{
...
ptr = /* alloca one object */;
...
}
else
{
...
ptr = /* alloca another object */;
...
}
语义上的差异可能至关重要。不管是不是你的情况 - 从你目前发布的内容我不能说。
另一个语义上不相关的差异是 memset
会将对象的所有字节置零,而 = { 0 }
不保证将填充字节(如果有)置零。如果该对象随后与一些基于二进制的 API 一起使用(例如发送到压缩的 I/O 流),这可能很重要。