エラーの場所が迷子になりました

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
あるる

エラーの場所が迷子になりました

#1

投稿記事 by あるる » 1年前

コード:

#include<stdio.h>
#include<stdlib.h>
#include <ctype.h>
#include <string.h>

#define WORD_LEN_MAX 256 

typedef struct node{
    char word[WORD_LEN_MAX];
    int count;
    int ASCII_word[WORD_LEN_MAX];
    struct node *next;   
}node_t;

node_t *list = NULL;

node_t *create_node(char *word){
    node_t *node;
    node = malloc(sizeof(node_t));
    if(node == NULL){
        printf("malloc eror\n");
    }
    strncpy(node->word, word, sizeof(node->word));
    node->count = 1;

    return node;

}

void enqueue_noode(node_t *node){
    node_t *tmp = list;
    while (tmp->next == NULL){
        tmp = tmp->next;
    }
    tmp->next = node;
    node->next = NULL;    
}

node_t *search_node(char *word){
    node_t *tmp = list;
    while(strcmp(tmp->word,word) != 0){
        if(tmp == NULL){
            break;
        }
        tmp = tmp->next;
    }
    return tmp;
}

void sort_node_by_word(void){
    node_t *tmp = list;
    int i;
    while(tmp != NULL){
        for(i = 0; i < strlen(tmp->word); i++){
            tmp->ASCII_word[i] = tmp->word[i];
        }
        tmp = tmp->next;
    }
    node_t prev;
    node_t *keep = NULL;
    prev.next = list;
    while (prev.next != NULL){
        node_t *premax = &prev;
        node_t *max = NULL;
        node_t *node;
        for(node = &prev; node->next != NULL; node = node->next){
            int j = 0;
            i = 0;
            while(j != 0)
            {
                if(premax->next->ASCII_word[i] < node->next->ASCII_word[i]){
                    premax = node;
                    j = 1;
                }else if(premax->next->ASCII_word[i] == node->next->ASCII_word[i]){
                    i++;
                }else{
                    break;
                }
            }
        }
        max = premax->next;
        premax->next = max->next;
        max->next = keep;
        keep = max;
    }
    list = keep;
}

void sort_node_by_count(void){
    node_t prev; 
    node_t *keep = NULL; 
    prev.next = list;
    while (prev.next != NULL) {
        node_t *premax = &prev; 
        node_t *max;
        node_t *node;
        for (node = &prev; node->next != NULL; node = node->next) {
            if (premax->next->count < node->next->count) {
                premax = node;
            }
        }
        max = premax->next;
        premax->next = max->next;
        max->next = keep;
        keep = max;
    }
    list = keep;
}

void print_list(void){
    node_t *tmp = list;
    while(tmp != NULL){
        printf("%s %d\n",tmp->word,tmp->count);
        tmp = tmp->next;
    }
}

int main(int argv,char *argc[]){
    node_t *node;
    //node_t *tmp = NULL;
    FILE *fp;
    if(argv != 3){
        printf("short or over comand\n");
        exit(1);
    }
    fp = fopen(argc[2],"r");
    if(fp == NULL){
        printf("file open error\n");
        exit(1);
    }
    int n;
    char buf[WORD_LEN_MAX];
    buf[0] = '\0';
    char c;
    int flag;
    while ((c = fgetc(fp)) != EOF){
        if('A' <= c && c <= 'Z' ){c = c - ('A' - 'a');}
        if(isalpha(c) != 0){
            n = strlen(buf);
            buf[n] = c;
            buf[n+1] = '\0';

            flag = 1;
        }else if(flag == 1){
            node = search_node(buf);
            if(node == NULL){
                printf("NULL ");
            }else{
                printf("%s ",node->word);
            }
            /*if(node == NULL){
                node = create_node(buf);
                node->next = tmp;
                tmp = node;
            }else{
                node->count++;
            }*/
            printf("%s\n",buf);
            buf[0] = '\0';
            flag = 0;     
        }
    }
    fclose(fp);

    if(strcmp(argc[1],"-o") == 0){
        print_list();
    }else if(strcmp(argc[1],"-c") == 0){
        //sort_node_by_count();
        print_list();
    }else if(strcmp(argc[1],"-w") == 0){
        //sort_node_by_word();
        print_list();
    }else{
        printf("comand error\n");
    }

    return 0;
}
実行時にコマンドとファイル名を引数に受け取り、ファイル内の英単語をその出現回数をカウントしコマンドに応じてソートして標準出力させたいです。ソートが正しく機能しているかの前にソート機能を使わないコマンド-oを設定してその他の機能が正常に動くかをチェックするため①search_nodeが正しく動いているか②正しくファイルを英単語ごとにbufに格納できているか を確認するprintfを設け、ソートを無効にしてコンパイル、実行しました。コンパイル時はエラーは無かったのですが何も出力されませんでした。main内145から150のsearch_nodeに関する部分を無効にし再度コンパイル、実行した結果期待していた結果が得られました。
詳しい原因を探すため145~150を有効にしGDBデバッガを用いて実行した((gdb)r -o -test.txt)結果、以下のようなエラーを受け取りました。
Program received signal SIGSEGV, Segmentation fault.
__strcmp_see42( )at../sysdeps/x84_64/multiarch/strcmp-see42.S:164
164 movdqu (%rdi), %xmm1
これは164行目のfclose(fp)でセグメンテーションエラーが出ているということでしょうか?
それともstrcmpの使い方にエラーが出ているのでしょうか?
再度main内145から150のsearch_nodeに関する部分を無効にしたうえで同様にGDBデバッガ内で実行した結果エラーメッセージは何も出ず、期待した結果とexited normaly(正常終了ですよね?)が出力されました。
やはりsearch_nodeの周辺がおかしいのでしょうか?
自分なりにデバッグを色々試してみたつもりだったのですが、原因はもちろん場所すら分からなくなり手詰まり状態です。助けて欲しいです。

アバター
usao
記事: 1887
登録日時: 11年前

Re: エラーの場所が迷子になりました

#2

投稿記事 by usao » 1年前

とりあえず実際にどう動いているのか細かく見ていけばどうか.
例えば,

node = search_node(buf);

を初めて実施する時点で

node_t *list = NULL;

が NULL のままだったりしたらヤバそうだよね.
そして実際そう動きそうにも見える(だれも list を更新しないであろうから).

あるる

Re: エラーの場所が迷子になりました

#3

投稿記事 by あるる » 1年前

初回だけ条件分岐ではなく、一旦create_node(ノードを作成する関数)を実行させ、これをlistに代入してから2回目以降の条件分岐に通せばよいとうことですか?
NULLに対する知識が浅く(とりあえず講義の指示通りに初期化しているだけ)、今回のようにNULLの状態から関数を始め、都度更新していく形は違反なのですか?

あるる

Re: エラーの場所が迷子になりました

#4

投稿記事 by あるる » 1年前

連続でごめんなさい。
ご指摘して頂けた箇所を中心に修正しました。

コード:

int main(int argv,char *argc[]){
    node_t *node;
    FILE *fp;
    if(argv != 3){
        printf("short or over comand\n");
        exit(1);
    }
    fp = fopen(argc[2],"r");
    if(fp == NULL){
        printf("file open error\n");
        exit(1);
    }
    int n;
    char buf[WORD_LEN_MAX];
    buf[0] = '\0';
    char c;
    int flag;
    //初回のみ必ずノードを生成させる
    while ((c = fgetc(fp)) != EOF){
        if('A' <= c && c <= 'Z' ){c = c - ('A' - 'a');}
        if(isalpha(c) != 0){
            n = strlen(buf);
            buf[n] = c;
            buf[n+1] = '\0';

            flag = 1;
        }else if(flag == 1){
            node = create_node(buf);
            node->next = NULL;
            list = node;
            break;
        }
    }
    printf("%s ",list->word);
    printf("%s\n",buf);
    buf[0] = '\0';
    flag = 0; 
    print_list();

    //2回目以降はsearch_nodeの結果によって条件分岐
    while ((c = fgetc(fp)) != EOF){
        if('A' <= c && c <= 'Z' ){c = c - ('A' - 'a');}
        if(isalpha(c) != 0){
            n = strlen(buf);
            buf[n] = c;
            buf[n+1] = '\0';

            flag = 1;
        }else if(flag == 1){
            node = search_node(buf);
            if(node == NULL){
                printf("NULL ");
            }else{
                printf("%s ",node->word);
            }
            /*if(node == NULL){
                node = create_node(buf);
                node->next = list
                list = node;  
            }else{
                node->count++;
            }*/
            printf("%s\n",buf);
            buf[0] = '\0';
            flag = 0;     
        }
    }
    fclose(fp);

    if(strcmp(argc[1],"-o") == 0){
        print_list();
    }else if(strcmp(argc[1],"-c") == 0){
        //sort_node_by_count();
        print_list();
    }else if(strcmp(argc[1],"-w") == 0){
        //sort_node_by_word();
        print_list();
    }else{
        printf("comand error\n");
    }

    return 0;
}
ちゃんと一個めのノードがlistに入っているかの確認のため、1回目のノード生成の後に、list->wordとbufを出力させ一致するか、print_listを実施させエラーがでないか を確認できるように一部追加しました。
main内のnode_t *tmpについては私がlistについて大きな勘違いをしていたため不要となり削除しました。
そのうえで症状変わらずです。
list = NULLではないlistを渡せているはずなのですがやはりsearch_nodeを有効にするとそれより下の命令が実行されません。
具体的な箇所でなくてもいいのでこのような場合に注意して確認しなおすべき点など教えて頂きたいです

アバター
usao
記事: 1887
登録日時: 11年前

Re: エラーの場所が迷子になりました

#5

投稿記事 by usao » 1年前

> とりあえず実際にどう動いているのか細かく見ていけばどうか.

の部分が言いたいことであり,以降は例である,という点をまずはご理解いただきたい.

【あなたは,このプログラムにある入力が与えられた際のあるべき動作を隅から隅まで知っているハズ】
なのだから,
そしたら1行ずつでもなんでも【あるべき動作をしているか?】をチェックしていけば良いのではありませんか?
そしたらある時点で,「なんでそう動くん?」っていう動作をすることを発見できるのではありませんか?

仮に,このコードがどこかからやってきたような代物であって,上記の【隅から隅まで知っている】をあなたが満たさない状況にあるのであれば,その状態でいたずらにコードをいじくっていても普通はどうにもならないので,まずは根本的なところ(=そのコードが扱おうとしている話,実装されているハズの処理の内容,etc)をちゃんと理解することから始めてください.

アバター
usao
記事: 1887
登録日時: 11年前

Re: エラーの場所が迷子になりました

#6

投稿記事 by usao » 1年前

それはそれとして,main関数で直接的に list の値を弄るのは正しいことですか?

おそらく,本来ならば変数 list は main関数からは見えない存在であるという前提のうえで,各種関数を使っていく想定なのだと想像しますが…

で,そしたら「各関数がきっちり己の役割を果たしているのか?」っていう話も当然ありますね.確認できていますか?
例えば,私から見ると,create_node関数は役割を十分に果たしているとは思えないのですが,実際のところどうなんでしょう?(実際の仕様はあなたにしかわからない)

アバター
usao
記事: 1887
登録日時: 11年前

Re: エラーの場所が迷子になりました

#7

投稿記事 by usao » 1年前

そもそもノードの型を main関数側が知らなきゃならない話(を扱っている状況)なのか?
というあたりから疑問というか謎.

main関数側から見たら

【具体実装がどうなってるかは知らんけども】とにかく「単語のリスト」というデータが(1つだけ)存在していて,それに対する限定的な操作(新しい単語を加える,ソートさせる)手段が提供されている

ではダメなのか?
(やりたいこと以上の自由度を設ける必要は無いように思うけど)

返信

“C言語何でも質問掲示板” へ戻る