1

私は20の質問に似たプログラムを実装しようとしています。このプログラムでは、質問のテキストファイルと回答の推測がロードされ、char配列にコピーされます(分割するために新しいスペース行が「/0」に置き換えられます)個々の文字列への質問)。テキストファイルがコピーされた後、配列は正常に機能します。ツリー構造は、フレーズを yes/no 質問ツリーに編成するように設定されています。左側の子は yes 応答で、右側は no 応答であり、葉は、プログラムが最後に推測するために使用する推測です。

私が抱えている問題は、ツリーを構築した後 (InitTree から treeBuilder を呼び出す)、テキスト ファイルからのフレーズがコピーされた配列の内容が破損したことです。

InitTree を呼び出す前は、配列の内容は次のようになっています。

毛むくじゃらですか?鳴きますか?猫 犬 牙はありますか?大きな耳はありますか?ゾウ サイ ワニ

呼び出した後、次のようになります。

毛むくじゃらですか?-???` ?p ?a 犬 牙はありますか? 大きな耳はありますか?ゾウ サイ ワニ

どこで機能しなくなるかをテストしてきました.treeBuilder内では、配列のすべての要素はそのままですが、treeBuilderへの関数呼び出しが終了するとすぐに、配列が破損します. メモリを割り当てるたびに calloc を使用してメモリを保護しようとしましたが、文字配列を静的にすることでさえ、これが発生したのと同様の状況で機能しました。しかし、私の予防策のすべてが機能しているようには見えず、どこに問題があるのか​​ わかりません. 私はすでにstackoverflowで同様のケースを見てみましたが、私の問題に関連することは何もできませんでした.

これは、プログラムが実際にツリーを使用し始めるときに、明らかな理由で最終的にセグ フォールトにつながります。

gdb を実行してみましたが、なんらかの理由で行情報を見つけることができず、入力を求めるプロンプトが表示されるか、メモリ エラーまたは何かが発生するまですべてをスキップするため、行ごとに移動できません。そのため、ここでは gdb を実行してもあまり役に立ちません。これは、メイン関数がインクルード ファイルなどにあるためではないかと推測しています。しかし、それは論外です。

問題に関連するコードは次のとおりです。

struct treeStruct {
    char *string;
    struct treeStruct *left, *right;
};

typedef struct treeStruct *TreeType;

// Builds a tree
void treeBuilder(TreeType tree, char **phrase, long level){
    // Gets the level (number of tabs) of the next phrase
    long nextLevel = countTabs(*phrase + strlen(*phrase) + 1);

    tree->string = *phrase + level; // Assigns the response pointer to the tree array

    // Move the pointer to the next string, since the the strings need to be
    // put into the tree in linear order
    (*phrase) += strlen(*phrase) + 1;

    if (level >= nextLevel){
    // Compares the current level with the level of the next string
            // to determine if returning up the tree is necessary;
            // This should be the answer to a question.
            tree->left = NULL;
            tree->right = NULL;
            return;
    }
    else{
            // Makes sure the left and right pointers of the struct have
            // allocated space
            tree->left = calloc(1, sizeof(TreeType));
            tree->right = calloc(1, sizeof(TreeType));

            // Adds the yes and no branches to the tree, recursion will take care
            // of adding sub-branches
            treeBuilder(tree->left, phrase, level + 1);
            treeBuilder(tree->right, phrase, level + 1);
    }

    return;

}


TreeType InitTree (char *file){
    if(file == NULL){
            printf("File '%s' does not exist.\n", file);
            exit(2);
    }

    FILE *fp;
    fp = fopen(file, "r");

    // Create a space in memory for the loaded questions to occupy
    static char *phrases;
    phrases = (char *)malloc(MAXSTR * MAXNUMQS * sizeof(char));

    copyText(fp, phrases);

    fclose(fp);

    // Create space in memory for the tree structure
    TreeType tree;
    tree = (TreeType) calloc(1, sizeof(TreeType));

    // Create a pointer to a pointer so that treeBuilder can
    // change what the first pointer is pointing to, so the strings in
    // phrases can be added in order throughout the recursion
    static char *phrase_ptr, **phrase_ptr2;
    phrase_ptr = &phrases[0];
    phrase_ptr2 = &phrase_ptr;

    //Build the tree
    treeBuilder(tree, phrase_ptr2, 0);

    topNode = tree;

    return tree;
}

これが tl;dr の場合は申し訳ありませんが、私の問題についてできるだけ明確にしたかったのです。

4

1 に答える 1

3

私が気づいたことの1つは、あなたが使用していることですがsizeof(TreeType)、それTreeTypeは構造体へのポインタであり、構造体自体ではありません。これは、どこも指していないポインターを作成していること、およびポインターを逆参照すると未定義の動作が発生することを意味します。残りの質問を読んだだけで、セグメンテーション違反が確実に説明されます。

構造体をポインターとして型定義しない方が良いと思います。ポインターの使用をより明確にします。

例えば。

typedef struct treeStruct TreeType;

void treeBuilder(TreeType *tree, char **phrase, long level){
    ...
    if (!tree->left) {
        // calloc returns a pointer to a new bit of memory that has been 
        // assigned on the heap
        TreeType *temp = calloc(1, sizeof(TreeType));
        // assignments below not explicitly needed as you're using calloc
        temp->string = NULL;
        temp->left = NULL;
        temp->right = NULL;

        tree->left = temp;
    }
    ...
}

typedef-ing ポインターに関する質問を次に示します。C では比較的一般的なようで、データ型が不透明であり、ユーザーが逆参照してはならない (ユーザーが渡す API 呼び出しによってのみ) ことを暗示するために使用されます。

于 2012-04-06T08:23:40.850 に答える