未知大小的动态数组C



我已经使用pjsip一段时间了,为了让它为UWP工作,我在基本C代码中遇到了一个看似致命的缺陷。我在下面的代码中得到错误'addrs': unknown size'deprecatedAddrs': unknown size

PJ_DEF(pj_status_t) pj_enum_ip_interface2( const pj_enum_ip_option *opt,
unsigned *p_cnt,
pj_sockaddr ifs[]) {
pj_enum_ip_option opt_;
if (opt)
opt_ = *opt;
else
pj_enum_ip_option_default(&opt_);
if (opt_.af != pj_AF_INET() && opt_.omit_deprecated_ipv6) {
pj_sockaddr addrs[*p_cnt];
pj_sockaddr deprecatedAddrs[*p_cnt];
unsigned deprecatedCount = *p_cnt;
unsigned cnt = 0;
int i;
pj_status_t status;
...

错误出现在两条线路pj_sockaddr addrs[*p_cnt];pj_sockaddr deprecatedAddrs[*p_cnt];上。我觉得我理解这不应该起作用,因为C不能有动态数组,需要预先分配空间。然而,pjsip是一个经常使用的成熟库,所以我的问题是:是否存在这样的情况?

这是有效的c99语法。它被称为可变长度数组。您可能希望将-std=c99标志添加到编译器中。

编辑:根据下面的注释,这里有一个关于如何使用动态内存来替换VLA的示例。

PJ_DEF(pj_status_t) pj_enum_ip_interface2( const pj_enum_ip_option *opt,
unsigned *p_cnt,
pj_sockaddr ifs[]) {
pj_enum_ip_option opt_;
if (opt)
opt_ = *opt;
else
pj_enum_ip_option_default(&opt_);
if (opt_.af != pj_AF_INET() && opt_.omit_deprecated_ipv6) {

/* Dynamically allocate memory to replace VLAs
* Use calloc if you need it initialized to zero
*/ 
pj_sockaddr *addrs = malloc((*p_cnt) * sizeof (pj_sockaddr));
pj_sockaddr *deprecatedAddrs = malloc((*p_cnt) * sizeof (pj_sockaddr));

unsigned deprecatedCount = *p_cnt;
unsigned cnt = 0;
int i;
pj_status_t status;
...

/* Remember that memory allocated from the heap needs to be freed */
free (addrs);
free (deprecatedAddrs);

/* Not needed, but a good practice */
addrs = NULL; 
deprecatedAddrs = NULL;
...

return status;

如果您使用的是gcc或clang,那么标志std=c99或类似的东西。

VLA:s来自C99,但在C11中是可选的。

如果你在使用微软风投,那么你就没有运气了。它不支持C99。这里有一个问题:Visual Studio 2017是否完全支持C99?

如果您想将代码更改为等效代码,那么可以使用alloca。所以改变

pj_sockaddr addrs[*p_cnt]; 

pj_sockaddr *addrs = alloca(sizeof *addrs * *p_cnt);

这基本上就是VLA:s引擎盖下发生的事情,所以这是一个快速解决方案。唯一的区别是,如果您稍后在代码中使用sizeof运算符,那么如果某个地方有一个循环,看起来像这样:

for(int i=0; i<sizeof addrs; i++) {
// Code
}

那你也得做点什么。

您可以使用malloc而不是alloca,但这可能会影响性能,之后您需要free内存。通常,我更喜欢使用malloc,但由于您不是从头开始写的,所以这并不重要。毕竟,由于原始代码使用的是VLA:s,因此不会添加任何问题。

简言之,VLA:s(和alloca(的问题在于,您不能错误检查分配,如果这样,您就有炸毁堆栈的风险。我在这里写了一个答案:https://stackoverflow.com/a/58163652/6699433

MSVC编译器不支持C99标准的所有功能,包括可变长度数组。您可以通过更改以下行使此代码与MSVC兼容:

pj_sockaddr addrs[*p_cnt];
pj_sockaddr deprecatedAddrs[*p_cnt];

使用malloc:

pj_sockaddr *addrs = malloc(*p_cnt * sizeof(pj_sockaddr));
pj_sockaddr *deprecatedAddrs = malloc(*p_cnt * sizeof(pj_sockaddr));

最新更新