0

テキストファイル( VSMと呼ばれる形式、したがって以下の名前)をツリー構造に解析するCアプリケーションを作成しています。この形式の設計者は、これをタグ付きツリーと呼んでいます。各ノードには、いくつかのキーと値のペア (または属性) と子ノードがあります。

以下は、問題の構造体と関数です。

vsm.h:

struct vsm_node {
    int                   numchildren;
    struct vsm_attribute *attrs   [36];
    struct vsm_node      *children[8];
};

void vsm_addchild(struct vsm_node *node, struct vsm_node *child);

vsm.c:

#include "vsm.h"

void vsm_addchild(struct vsm_node *node, struct vsm_node *child)
{
    node->children[node->numchildren] = child;
    ++(node->numchildren);
}

呼び出すvsm_addchildとセグメンテーション違反が発生するのはなぜですか?

これがばかげた質問なら申し訳ありませんが、私は単純な C が本当に苦手です。特にポインタとメモリ管理に関係する場合はなおさらです。


編集して、呼び出しを行うコードを含めます。

#include <stdio.h>
#include "vsm.h"

void vsm_parse(struct vsm_node *tree, FILE *fp, char *name)
{
    struct vsm_node *this     = tree;

    int ch;
    while ((ch = fgetc(fp)) != 0) {
        ...
        else if (ch == '{') {
            struct vsm_node *node;
            vsm_initnode(node);
            vsm_addchild(this, node);
            this = node;
            ...
        ...

編集:機能を追加しましたvsm_initnodeが、正しく行っているかどうかわかりません。

void vsm_initnode(struct vsm_node *node)
{
    node              = malloc(     sizeof(struct vsm_node *));
    node->attrs       = malloc(36 * sizeof(struct vsm_attribute *));
    node->children    = malloc( 8 * sizeof(struct vsm_node *));
    node->numchildren = 0;

    int i;
    for (i = 0; i < 36; ++i)
        node->attrs[i] = NULL; /* unnecessary? */
    for (i = 0; i  < 8; ++i)
        node->children[i] = NULL;
}

void vsm_addchild(struct vsm_node *node, struct vsm_node *child)
{
    node->children[node->numchildren] = child;
    ++(node->numchildren);
}

gdb 出力:

Program received signal SIGSEGV, Segmentation fault.
vsm_addchild (node=0x28, child=0x7541612d <msvcrt!_atodbl_l+2294>) at vsm.c:62
62              node->children[node->numchildren] = child;
4

3 に答える 3

1

ノード ポインタを正しく初期化していません。これは、node渡している変数が無効であり、ランダムにメモリを指していることを意味します。ほぼ確実に、プロセスが所有していないメモリを指しており、セグメンテーション違反が発生しています。代わりにこれを試してください:

// Change (1): new return type, parameter removed
struct vsm_node* vsm_initnode()
{
    struct vsm_node* node;
    node              = malloc(     sizeof(struct vsm_node)); // Change (2)

    // The following lines are unnecessary - change (3)
    //node->attrs       = malloc(36 * sizeof(struct vsm_attribute *));
    // node->children    = malloc( 8 * sizeof(struct vsm_node *));

    node->numchildren = 0;

    int i;
    for (i = 0; i < 36; ++i)
        node->attrs[i] = NULL; // unnecessary but good practice
    for (i = 0; i  < 8; ++i)
        node->children[i] = NULL;

    return node;
}

void vsm_addchild(struct vsm_node *node, struct vsm_node *child)
{
    node->children[node->numchildren] = child;
    ++(node->numchildren);
}

各エラーの説明:

struct vsm_node*(1) - 最初はパラメータとして関数に渡していました。これにより、ポインターが参照する値を変更できます。ただし、ポインター自体を変更することはできますが (への呼び出しによる場合と同様malloc)、それらの変更は呼び出し元には反映されません。メモリの割り当てと初期化はすべて無駄になりました。

(2) - 型のポインターはstruct vsm_node*struct vsm_node. 代わりに、以前は を保持するのに十分なメモリを割り当てていましたがstruct vsm_node*、これは十分な大きさではありません。経験則として、malloc呼び出しの右側には、左側よりも間接化のレベルを 1 つ少なくする必要があります。つまり、代入先の変数に type がある場合T**sizeof内部の呼び出しは typemallocを参照する必要がT*あり、任意の数の*文字について同様です。

(3) 構造体の定義は次のとおりです。

struct vsm_node {
    int                   numchildren;
    struct vsm_attribute *attrs   [36];
    struct vsm_node      *children[8];
};

これは、 type の変数を使用するか、単に宣言するstruct vsm_nodeかにかかわらず、オブジェクトを割り当てるときに、ポインターの両方の配列に対して十分なメモリが既にあることを意味します。より多くのメモリを割り当てることは、不必要であるだけでなく無駄です。また、追加のメモリを割り当てて配列を使用した場合とは、プログラムの動作が異なる場合もあります。mallocstruct vsm_node

于 2013-09-02T04:14:16.967 に答える