質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

C言語でリスト構造を用いたワードカウンタを作成したい

受付中

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 733

Nanora_s

score 10

前提・実現したいこと

大学の課題でリスト構造を用いたワードカウンタ(文章を読み込み、単語を一つずつ認識して文章中に何回出てきたかを出力するプログラム)を作成しています。
リスト構造がいまいち良く分からないまま頑張ってプログラムに追加していましたが、segmentation faultが出て手詰まりになってしまいました。
このプログラムをちゃんとした形で動かしたく、ご助力をお願いします。

発生している問題・エラーメッセージ

リスト構造を用いたところ、各関数に新たな引数を設定したのでそれをmain関数内の足りないと出てきたところを補ったらsegmentation faultが出ました。

それ以外ですと
wordlist.c: In function ‘get_word’:
wordlist.c:24:2: warning: null argument where non-null required (argument 2) [-Wnonnull]
  strcpy(list->word,'\0');
が出ました。

該当のソースコード

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

FILE *fp;
char word[1024];

struct LIST{//単語を登録する構造体
    char word[1024];//単語
    int count;
    struct LIST *next;
};

int get_word(struct LIST *list){
    char letter;//文章内の単語

    letter = fgetc(fp);
    while(!(letter == ' ' || letter == EOF || letter == ',' || letter == '.')){
        //この条件が成り立たない場合
        strcpy(word,list->word);//単語をwordリストに入れる
        list->count ++;//リスト番号を1増やす
        letter = fgetc(fp);//これ初期化的な奴?
        }
    strcpy(list->word,'\0');
}
int list_length = 0;

void count_up(struct LIST *list){
    int i;

    for(i =0;i< list_length;i++){//単語数だけ繰り返す
        if(strcmp(list->word,word)){//もし同じ単語なら
            list[i].count++;//カウントする
            return ;
        }
    }
    list[i].count = 1;
    strcpy(list->word,word);
    list_length++;
    return;
}

void output(struct LIST *list){
    int i;
    for(i = 0;i<list_length;i++){
        printf("%s : %d\n",list->word, list[i].count);
    }
    return ;

}

    int main(struct LIST *list){

    int ret;

    fp = fopen("input.txt" , "r");
    ret = get_word(list);
    printf("%s\n",list->word);

    while(ret == 1){
        count_up(list);
        ret = get_word(list);
    printf("%s\n",list->word);
    }
    output(list);
    return 0;
}

試したこと

とにかくエラーをなくすことに専念したのでエラー表示が出なくなってからは何がおかしいのか確認できないくなってしまいました。

補足情報(FW/ツールのバージョンなど)

使用しているのはvimエディタでコンパイラはgccです。
windows10に標準搭載のLinax(Ubuntu/Bash)環境で作業しています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • episteme

    2019/03/16 16:51

    struct LIST の総数は単語数に伴って増加するはずよね? どこで増やして(=mallocして)ます?

    キャンセル

回答 4

0

main関数の引数は言語仕様上、引数の個数、引数の値(つまりint argc, char *argv[]、もしくはvoid)と決まっています。
そこにstruct LIST *listという引数を書いているので、本来のargcがlistに置き換わっておりおかしな動作になっています。
listはmain関数の中で定義し、メモリの割り当てをする必要があります。

int main(void)
{
    struct LIST *list;
    int ret;
    list = (struct LIST*)malloc(sizeof(struct LIST));

    fp = fopen("input.txt" , "r");

またリスト構造とのことですが、構造体内のnextが使われていません。
countも同じ構造体内にあったら意味がないようように思えますし。
全体的に見直しが必要です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/16 16:02

    count はワードの出現数を数えるカウンタではないでしょうか.
    おそらく
    > list->count ++;//リスト番号を1増やす
    が間違っていて
    > list[i].count++;//カウントする
    が本命(?)と思います.

    キャンセル

  • 2019/03/16 16:04

    要素の中にカウント持ってたら、各要素にカウントが入ることになるので意味がないと思うのですよ。

    キャンセル

  • 2019/03/16 16:06

    何番目かを表すという意味ではあってもいいですけど。
    少なくとも全体の個数を表す意味では置き場所は違いますね。

    キャンセル

0

間違いが多く
また、関数同士の意図がバラバラで歪です。
(例: get_wordは読み取った単語をどこに入れますか?返り値として何を想定していますか?
どういった状態の場合に元の関数に戻ることを想定していますか?)

正直、設計段階からやり直した方がよいです。

ざっくりとした雛形だけ作ると以下

/* TODO: fpから1単語を読み出しdestにコピーする関数を書くこと
  戻り値として、読み込めた時は0以外、読み込めなかった時は0を返す
  destに書き込んだ文字数でも返しとけばちょうどいい
*/
int get_word(FILE* fp, char* dest){
}

/* TODO: listとwordを受け取って
  list内にwordがあればカウントを1増やす
              なければリストの末尾にmallocもしくはcallocで増やす
*/
void cout_up(LIST* list, char* word){
}


/* TODO: listの内容を出力する
*/
void output(LIST* top){
}
int main(void){
  FILE* fp = fopen("input.txt", "r");
  LIST list = {};
  char word[1024];
  while(get_word(fp, word)){ // wordが読み取れる間
    puts(word);
    count_up(list, word);    // wordをlistに追加
  }
  output(list);
}

リストについて

list[i].count これではlistは単なる配列です。
リスト構造といった場合のリストはおそらく線形リストであり
list = list->nextで次の要素にアクセスしていく奴です。

考える上でちょっと難しいのが何も入っていない初期状態をどうするかです。
まぁ、今回の場合は「1個分を次の単語のために予約する」のがラクな気がします。

// 追加時
LIST* current = top;
LIST* next = current->next;
if(!next) { // 次の要素がない
  strcpy(now->word, word); // 単語を登録
  now->next = あたらしい要素を予約し次の要素として登録
}
current = next;

的な

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

こういうことかと思うのですが. あっているでしょうか.

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

struct LIST{
    char word[1024];
    int count;
    struct LIST *next;
};

int get_word(FILE *fp, char *word) {
    int i, v;

    i = 0;
    while(1) {
        v = fgetc(fp);
        if(v == -1) break;
        if(v == ' ' || v == ',' || v == '.' || v == '\r' || v == '\n') {
            if(i == 0) continue;
            break;
        }
        word[i] = (char)(v & 0xff);
        i++;
    }
    word[i] = '\0';
    return i;
}


struct LIST *create_item(char *word, struct LIST *next) {
    struct LIST *list;
    list = (struct LIST *)malloc(sizeof(struct LIST));
    strcpy(list->word, word);
    list->count = 1;
    list->next = next;
    return list;
}

struct LIST *count_up(struct LIST *root, char *word) {
    struct LIST *list;

    for(list = root; list != NULL; list = list->next) {
        if(strcmp(list->word, word) == 0) {
            list->count ++;
            return root;
        }
    }
    return create_item(word, root);
}

void output(struct LIST *list){
    int i = 0;
    for(; list != NULL; list = list->next) {
        printf("%s : %d\n", list->word, list->count);
        i ++;
    }
    printf("-- %d items\n", i);
}

int main(int argc, char *argv[]) {
    int ret;
    FILE *fp;
    char word[1024];
    struct LIST *root;

    fp = fopen("input.txt", "r");
    while(1) {
        ret = get_word(fp, word);
        if(ret == 0) break;
        root = count_up(root, word);
    }
    fclose(fp);

    output(root);

    return 0;
}

リスト構造がいまいち良く分からないまま頑張ってプログラムに追加していました

構造をしっかり理解せずにコードを書いても, 例え動いたとしても分からないままでは無いでしょうか.
リストは言葉よりも図にしたほうが分かりやすい類では無いかと思いますので, 図によって解説している書籍やサイトを探してみては如何でしょう.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/16 17:49

    asm さんのざっくり雛形とそっくりに^^

    キャンセル

0

誰が書いても似たようになっちゃうかー...

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

struct LIST{//単語を登録する構造体
    char word[1024];//単語
    int count;
    struct LIST *next;
};

/* fpから1単語を切り出して返す */
const char* get_word(FILE* fp) {
  static char word[256];
  int ch;
  char letter[2];
  letter[1] = '\0';
  word[0] = '\0';

  /* 空白文字をskipする */
  do {
    ch = fgetc(fp);
    if ( ch  == EOF ) return NULL;
  } while( ch == ' ' || ch == ',' || ch == '.' );

  /* 空白文字までをwordに取り込む */
  while ( !(ch == ' ' || ch == ',' || ch == '.' || ch == EOF) ) {
    letter[0] = ch;
    strcat(word, letter);
    ch = fgetc(fp);
  }
  return word;
}

/* list中にwordがあれば当該LISTのcountを+1する。
   さもなくば新たなLISTを確保し先頭に挿入する
 */
struct LIST* count_up(struct LIST *list, const char* word) {
  struct LIST* result = list;
  while ( list != NULL ) {
    if ( strcmp(list->word,word) == 0 ) {//もし同じ単語なら
      ++list->count;//カウントして
      return result; // おしまい
    }
    list = list->next;
  }
  // 新たなLISTを(先頭に)追加する
  list = result;
  result = (struct LIST*)malloc(sizeof(struct LIST));
  strcpy(result->word,word);
  result->count = 1;
  result->next = list;
  return result;
}

void output(const struct LIST *list){
  while ( list != NULL ) {
    printf("%s : %d\n",list->word, list->count);
    list = list->next;
  }
}

int main() {
  struct LIST* list = NULL;
  const char* word;
  FILE* fp = fopen("input.txt" , "r");

  while ( (word = get_word(fp)) != NULL ) {
    list = count_up(list, word);
  }
  fclose(fp);

  output(list);

  // listの各要素を廃棄する
  while ( list != NULL ) {
    struct LIST* next = list->next;
    free(list);
    list = next;
  }
  return 0;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/17 00:45

    あぁ, fclose 入れたのに free 忘れてました ><

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る