You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义node_t类型可变参数C函数多参数时段错误问题

问题拆解与修复方案

兄弟,先看你代码里最扎眼的问题:你重复定义了add_node函数!同一个编译单元里两个签名完全一样的函数,C语言绝对不允许这种操作——要么编译报错,要么编译器偷偷覆盖掉前面的定义,导致你第一个函数里的类型判断逻辑根本没被执行,直接跑了第二个简化版的函数,这本身就是崩溃的潜在诱因。

接下来咱们逐个解决问题:

1. 先干掉重复定义的函数

删掉第二个add_node,只保留带switch类型判断的那个(毕竟这才符合你按类型存数组的需求)。

2. 可变参数必须加终止标记

你调用add_node的时候没传NULL结尾啊!可变参数函数本身不知道你传了多少个参数,必须手动加一个NULL当“结束符”,不然va_arg会一直读栈上的垃圾数据,读到非法内存直接就段错误了。所以调用的时候要改成:

add_node(mp, ROOT_NODE, NODE_A, NODE_B, NODE_C, NULL);

3. 循环逻辑的坑

你第一个函数的do-while循环逻辑有问题:

  • 先取了第一个参数处理,然后while里又取一次下一个参数,判断满足条件才进入下一次循环。但如果没传NULL,最后会读到垃圾值,直接崩。
  • 第二个函数更坑,直接把第一次取的参数丢了,根本没处理第一个传入的节点(NODE_A)。

修正后的循环应该直接遍历每个参数,直到遇到NULL,同时跳过和目标节点key相同的节点:

node_t *n;
while ((n = va_arg(lmnts, node_t *)) != NULL) {
    if (apr_strnatcmp(n->key, p_target_node->key) == 0) {
        continue;
    }
    // 处理节点...
}

4. 数组push的类型转换

你原来写的*(const node_t**)apr_array_push(tbl) = n;里的const没必要,反而容易出问题,直接写成*(node_t**)apr_array_push(tbl) = n;就行,因为apr_array_push返回的是指向数组新元素的void*,强转成node_t**后赋值即可。


修复后的完整代码

下面是修正后的完整代码,我还加了点错误处理,比如遇到未知类型的节点会打印错误并跳过:

#include <stdarg.h>
#include <stdio.h>
#include <apr_general.h>
#include <apr_hash.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <apr_tables.h>

typedef enum node_type_t {
    node_type_string,
    node_type_number,
} node_type_t;

typedef struct node_t {
    char *key;
    node_type_t type;
    apr_array_header_t *arr_strings;
    apr_array_header_t *arr_numbers;
} node_t;

node_t *create_node(apr_pool_t *p_pool, char *p_key, node_type_t p_type) {
    node_t *NODE = apr_palloc(p_pool, sizeof(node_t));
    NODE->key = p_key;
    NODE->type = p_type;
    // 初始化数组时给点初始容量,避免频繁扩容
    NODE->arr_strings = apr_array_make(p_pool, 2, sizeof(node_t*));
    NODE->arr_numbers = apr_array_make(p_pool, 2, sizeof(node_t*));
    return NODE;
}

int add_node(apr_pool_t *p_pool, node_t *p_target_node, ...) {
    int rv = 0;
    va_list lmnts;
    va_start(lmnts, p_target_node);
    
    node_t *n;
    // 遍历所有参数,直到遇到NULL终止符
    while ((n = va_arg(lmnts, node_t *)) != NULL) {
        // 跳过和目标节点key相同的节点
        if (apr_strnatcmp(n->key, p_target_node->key) == 0) {
            continue;
        }
        
        apr_array_header_t *tbl = NULL;
        switch(n->type) {
            case node_type_string:
                tbl = p_target_node->arr_strings;
                break;
            case node_type_number:
                tbl = p_target_node->arr_numbers;
                break;
            default:
                fprintf(stderr, "⚠️ Unknown node type for key: %s\n", n->key);
                continue;
        }
        
        if (!tbl) {
            continue;
        }
        
        printf("\t\t[%d] Added node: %s\n", rv, n->key);
        *(node_t**)apr_array_push(tbl) = n;
        rv++;
    }
    
    va_end(lmnts);
    return rv;
}

int main(int argc, const char *argv[]) {
    apr_status_t rv;
    apr_pool_t *mp;
    rv = apr_initialize();
    if (rv != APR_SUCCESS) {
        fprintf(stderr, "Failed to initialize APR\n");
        return -1;
    }
    apr_pool_create(&mp, NULL);
    
    node_t *ROOT_NODE = create_node(mp, "THE_ROOT", node_type_string);
    printf("\tROOT_NODE { key: '%s', type: %d, strings: %d, numbers: %d}\n", 
           ROOT_NODE->key, ROOT_NODE->type, 
           ROOT_NODE->arr_strings->nelts, ROOT_NODE->arr_numbers->nelts);
    
    node_t *NODE_A = create_node(mp, "A", node_type_string);
    printf("\tNODE_A { key: '%s', type: %d, strings: %d, numbers: %d}\n", 
           NODE_A->key, NODE_A->type, 
           NODE_A->arr_strings->nelts, NODE_A->arr_numbers->nelts);
    
    node_t *NODE_B = create_node(mp, "B", node_type_number);
    printf("\tNODE_B { key: '%s', type: %d, strings: %d, numbers: %d}\n", 
           NODE_B->key, NODE_B->type, 
           NODE_B->arr_strings->nelts, NODE_B->arr_numbers->nelts);
    
    node_t *NODE_C = create_node(mp, "C", node_type_string);
    printf("\tNODE_C { key: '%s', type: %d, strings: %d, numbers: %d}\n", 
           NODE_C->key, NODE_C->type, 
           NODE_C->arr_strings->nelts, NODE_C->arr_numbers->nelts);
    
    // 注意最后必须加NULL作为可变参数的终止标记!
    int added_count = add_node(mp, ROOT_NODE, NODE_A, NODE_B, NODE_C, NULL);
    printf("\n✅ Added %d nodes total\n", added_count);
    printf("\tROOT_NODE now has %d string nodes, %d number nodes\n", 
           ROOT_NODE->arr_strings->nelts, ROOT_NODE->arr_numbers->nelts);
    
    apr_pool_destroy(mp);
    apr_terminate();
    return 0;
}

现在这个代码应该能正常运行,不会再出现段错误,而且能正确把NODE_A、NODE_C(字符串类型)加到ROOT的arr_strings,NODE_B(数字类型)加到arr_numbers里。

内容的提问来源于stack exchange,提问作者Kérdezősködő Indián

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:01:33