在解析树中打印第三个字符串时出现段错误

时间:2013-09-07 17:29:12

标签: c string tree segmentation-fault unions

我正在创建一个编译器,并开始创建我的解析树结构。

我有一个'节点',它可以包含或不包含子节点。

typedef struct Node {
    int node_type;
    union {
        char* string;
        int number;
        struct Node* nodes;
    } node_data;
} Node;

这些功能组装/打印

Node* MakeNodeFromString(char* mystring) {
    Node* mynode = (Node*) malloc(sizeof(Node));
    mynode->node_data.string = strdup(mystring);
    mynode->node_type = 0; // @TODO not 3
    return mynode;
}

Node* MakeTwoBranchNode(int nodetype, Node* a, Node* b) {
    Node* mynode = (Node*) malloc(sizeof(Node));
    mynode->node_type = 2; // @TODO not 3
    mynode->node_data.nodes = malloc(2 * sizeof(Node*));
    mynode->node_data.nodes[0] = *a; mynode->node_data.nodes[1] = *b;
    return mynode;
}

void printtree (Node *n, int level) {
    if (!n) return;

    printf ("start %d\n", n->node_type);
    switch (n->node_type) {
        case 2:
            printf ("%*c2\n", level, ' ');
            printtree (&n->node_data.nodes[0], level+1);
            printtree (&n->node_data.nodes[1], level+1);
            break;
        case 0:
            printf ("%*c%s\n", level, ' ', n->node_data.string);
            break;
    }
    printf ("end %d\n", n->node_type);
}

每当我组装一棵树时,我会得到段错误,无论是printf'ing还是strlen'ing我的字符串。我已经尝试过strdup,strcpy等等。我很确定它的MakeTwoBranchNode没有失败,因为我可以创建大型数字树(不包含代码)。但我不确定。

这是一个代码示例,它在我的机器上执行 - 并且不会 - 段错误

int main() {
    // Works
    printtree(
        MakeTwoBranchNode(3,
            MakeNodeFromString("first string"),
            MakeNodeFromString("second string")
        ),
        1
    );
    // Fails
    printtree(
        MakeTwoBranchNode(3,
            MakeTwoBranchNode(3,
                MakeNodeFromString("first string"),
                MakeNodeFromString("second string")
            ),
            MakeNodeFromString("third string")
        ),
        1
    );
}

如果你运行这个例子(并且可以理解它的神秘输出),你会在printf(n-> node_data.string)中看到它的段错误。

2 个答案:

答案 0 :(得分:3)

你在下面分配了sizeof-a-pointer,而不是sizeof-a-node:

Node* MakeTwoBranchNode(int nodetype, Node* a, Node* b) {
    Node* mynode = malloc(sizeof(Node));
    mynode->node_type = 2; // @TODO not 3
    mynode->node_data.nodes = malloc(2 * sizeof(Node*)); // HERE
    mynode->node_data.nodes[0] = *a; mynode->node_data.nodes[1] = *b;
    return mynode;
}

您可以将上面的注释行更改为:

    mynode->node_data.nodes = malloc(2 * sizeof(Node));

但是,将您的测试程序写成内存泄漏。您为传递MakeTwoBranchNode()的节点分配的内存会发生什么变化?这不是我认为你真正想要的。首先使用指针数组会更好。

typedef struct Node {
    int node_type;
    union {
        char* string;
        int number;
        struct Node *nodes[2];
    } node_data;
} Node;

然后保存传递给MakeTwoBranchNode的实际指针。在这样做时,您将这些节点的所有权传递给双分支节点(因此您还应确保何时释放它以正确清理其子节点):

Node* MakeTwoBranchNode(int nodetype, Node* a, Node* b) {
    Node* mynode = malloc(sizeof(Node));
    mynode->node_type = 2; // @TODO not 3
    mynode->node_data.nodes[0] = a; 
    mynode->node_data.nodes[1] = b;
    return mynode;
}

现在没有内存泄漏,除非你在销毁双分支节点时free()nodes[0]中没有nodes[1]指针。

答案 1 :(得分:2)

typedef struct Node {
    int node_type;
    union {
        char *string;
        int number;
        struct Node **nodes; // <-- here
    } node_data;
} Node;


Node *MakeNodeFromString(char *mystring) {
    Node *mynode = malloc(sizeof *mynode);
    mynode->node_data.string = strdup(mystring);
    mynode->node_type = 0; // @TODO not 3
    return mynode;
}

Node *MakeTwoBranchNode(int nodetype, Node *a, Node *b) {
    Node *mynode = malloc(sizeof *mynode);
    mynode->node_type = 2; // @TODO not 3
    mynode->node_data.nodes = malloc(2 * sizeof *mynode->node_data.nodes ); // <- here
    mynode->node_data.nodes[0] = *a; mynode->node_data.nodes[1] = *b;
    return mynode;
}

void printtree (Node *n, int level) {
    if (!n) return;

    printf ("start %d\n", n->node_type);
    switch (n->node_type) {
        case 2:
            printf ("%*c2\n", level, ' ');
            printtree (n->node_data.nodes[0], level+1); // <-- here
            printtree (n->node_data.nodes[1], level+1); // <- and here
            break;
        case 0:
            printf ("%*c%s\n", level, ' ', n->node_data.string);
            break;
    }
    printf ("end %d\n", n->node_type);
}