动态数组未知大小 C
Dynamic array unknown size 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 不能有动态数组并且需要预分配 space。但是,pjsip 是一个经常使用的成熟库,所以我的问题是:是否存在它可以正常工作的情况?
这是有效的 c99 语法。它被称为 variable-length array。您可能希望将 -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;
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));
如果您使用的是 gcc 或 clang,则标志 std=c99
或类似的东西。
VLA:s 来自 C99,但在 C11 中被设为可选。
如果您使用的是 MSVC,那您就不走运了。它不支持 C99。这里有一个问题:Does Visual Studio 2017 fully support 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)的问题是您不能对分配进行错误检查,否则您就有炸毁堆栈的风险。我在这里写了一个答案:
我使用 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 不能有动态数组并且需要预分配 space。但是,pjsip 是一个经常使用的成熟库,所以我的问题是:是否存在它可以正常工作的情况?
这是有效的 c99 语法。它被称为 variable-length array。您可能希望将 -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;
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));
如果您使用的是 gcc 或 clang,则标志 std=c99
或类似的东西。
VLA:s 来自 C99,但在 C11 中被设为可选。
如果您使用的是 MSVC,那您就不走运了。它不支持 C99。这里有一个问题:Does Visual Studio 2017 fully support 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)的问题是您不能对分配进行错误检查,否则您就有炸毁堆栈的风险。我在这里写了一个答案: