【问题标题】:Dynamic array unknown size C动态数组未知大小 C
【发布时间】:2020-11-13 17:53:17
【问题描述】:

我使用 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 是一个非常成熟的库,经常使用,所以我的问题是:在这种情况下它是否会起作用?

【问题讨论】:

    标签: c uwp pjsip pjsua2


    【解决方案1】:

    这是有效的 c99 语法。它被称为variable-length array。您可能希望将-std=c99 标志添加到您的编译器。

    编辑: 鉴于下面的 cmets,这里有一个关于如何使用动态内存替换 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;
    

    【讨论】:

    • 啊好吧,有没有办法在visual studio中切换到C99?这是更大解决方案的一部分。
    • 这是另一个问题。 MSVC 不支持 VLA。
    • @dbush MSVC 不应该符合标准吗?
    • 由于不支持“VLA”,我是否应该更改代码?
    • @TonyTannous MSVC 不完全符合标准。
    【解决方案2】:

    如果您使用 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)的问题在于您无法对分配进行错误检查,否则您可能会导致堆栈崩溃。我在这里写了一个答案:https://stackoverflow.com/a/58163652/6699433

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 尺寸不对。它应该是(*p_cnt) * sizeof (pj_sockaddr)
      • 仍然缺少sizeof 参数
      猜你喜欢
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-03
      • 1970-01-01
      • 2011-10-24
      相关资源
      最近更新 更多