Variadic C функция с пользовательским типом - PullRequest
0 голосов
/ 14 мая 2018

Я хочу реализовать переменную функцию, которая получает параметры нестандартного типа, не такие как в примере с GNU:

https://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html#Variadic-Example

В моей логике я обрабатываю узлы, определяемые этим tpye:

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

Такой узел хранит информацию в разных массивах APR на основе определенного типа (строки, числа), определенного таким образом:

typedef enum node_type_t {
    node_type_string,
    node_type_number,
} node_type_t;

Функция выглядит следующим образом:

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 = va_arg(lmnts, node_t *);
    apr_array_header_t *tbl;
    do {
        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:
                break;
        }
        printf("\t\t%d - %s\n", rv, n->key);
        *(const node_t**)apr_array_push(tbl) = n;
        rv++;
    } while (n && ((n = va_arg(lmnts, node_t *)) != NULL) &&   apr_strnatcmp(n->key, p_target_node->key) != 0);
    va_end(lmnts);
    return rv;
}

Работает для двух 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, 0, sizeof(node_t*));
    NODE->arr_numbers = apr_array_make(p_pool, 0, 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 = va_arg(lmnts, node_t *);
    apr_array_header_t *tbl;
    do {
        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:
                break;
        }
        printf("\t\t%d - %s\n", rv, n->key);
        *(const node_t**)apr_array_push(tbl) = n;
        rv++;
    } while (n && ((n = va_arg(lmnts, node_t *)) != NULL) && apr_strnatcmp(n->key, p_target_node->key) != 0);
    va_end(lmnts);
    return rv;
}

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 = va_arg(lmnts, node_t *);
    while((n = va_arg(lmnts, node_t *)) != NULL && apr_strnatcmp(n->key, p_target_node->key) != 0) {
        printf("\t\t%d - %s\n", rv, n->key);
        *(const node_t**)apr_array_push(p_target_node->arr_strings) = 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) {
        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, [%d, %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, [%d, %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, [%d, %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, [%d, %d]}\n", 
        NODE_C->key, NODE_C->type,
        NODE_C->arr_strings->nelts, NODE_C->arr_numbers->nelts);

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

    printf("\tn = %d, n = %d\n", ROOT_NODE->arr_strings->nelts, ROOT_NODE->arr_numbers->nelts);

    apr_pool_destroy(mp);
    apr_terminate();
    return 0;
}

1 Ответ

0 голосов
/ 28 мая 2018

Проблема, которую я вижу, заключается в том, что ваш add_node(), кажется, проверяет аргумент NULL, чтобы определить, что список аргументов составлен, но вы никогда не передаете аргумент NULL при его вызове, поэтому add_nodeпродолжит читать несуществующие «аргументы» после того, как пройдёт все те, которые вы передали, получит доступ к местам в памяти, которые не должны, и вызовет всевозможные плохие проблемы.

...