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

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

ただいまの
回答率

89.12%

プログラムをコンパイルして、コマンドを実行できるのですが、数値を打ち込んだ時点で、止まります。

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,063

sanchu52

score 180

プログラムをコンパイルして、コマンドを実行できるのですが、数値を打ち込んだ時点で、止まります。下のほうに実行結果をのせています。どこが悪いのでしょうか。
よろしくお願いいたします。

コード
// 双方向循環リストリスト 

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


// コマンド 
enum Cmd_tag {
    CMD_ADD,
    CMD_DELETE,
    CMD_SEARCH,
    CMD_CLEAR,
    CMD_PRINT,
    CMD_EXIT,

    CMD_NUM
};

// コマンド文字列の種類 
enum CmdStr_tag {
    CMD_STR_SHORT,
    CMD_STR_LONG,

    CMD_STR_NUM
};

// コマンドの戻り値 
enum CmdRetValue_tag {
    CMD_RET_VALUE_CONTINUE,
    CMD_RET_VALUE_EXIT,
};

// コマンド文字列 
static const char* const CMD_STR[CMD_NUM][CMD_STR_NUM] = {
    { "a", "add" },
    { "d", "delete" },
    { "s", "search" },
    { "c", "clear" },
    { "p", "print" },
    { "e", "exit" }
};


static void init_head(void);
static void print_explain(void);
static void print_blank_lines(void);
static enum CmdRetValue_tag get_cmd(void);
static enum CmdRetValue_tag cmd_add(void);
static enum CmdRetValue_tag cmd_delete(void);
static enum CmdRetValue_tag cmd_search(void);
static enum CmdRetValue_tag cmd_clear(void);
static enum CmdRetValue_tag cmd_print(void);
static enum CmdRetValue_tag cmd_exit(void);
static void add_elem(int value);
static int delete_elem(int value);
static void clear_list(void);
static void print_list(void);
static struct LinkedList_tag* search_tail(void);
static struct LinkedList_tag* search_elem(int value);
static struct LinkedList_tag* delete_one_node(struct LinkedList_tag* node);

static void get_line(char* buf, size_t size);

// コマンド実行関数 
typedef enum CmdRetValue_tag (*cmd_func)(void);

static const cmd_func CMD_FUNC[CMD_NUM] = {
    cmd_add,
    cmd_delete,
    cmd_search,
    cmd_clear,
    cmd_print,
    cmd_exit
};

// 連結リスト型 
struct LinkedList_tag {
    int                     value;
    struct LinkedList_tag*  next;
    struct LinkedList_tag*    prev;    
};

static struct LinkedList_tag gHead ;//先頭の要素
// 先頭要素を初期化 
void init_head(void)
{

    gHead.value = 0;
    gHead.next    =NULL; 
    gHead.prev    =NULL;    
}

int main(void)
{
    init_head();

    while( 1 ){
        print_explain();

        if( get_cmd() == CMD_RET_VALUE_EXIT ){
            break;
        }

        print_blank_lines();
    }

    return 0;
}


// 説明文を出力
void print_explain(void)
{
    puts( "コマンドを入力して下さい。" );
    printf( " 連結リストに要素を追加する: %s (%s)\n", CMD_STR[CMD_ADD][CMD_STR_SHORT], CMD_STR[CMD_ADD][CMD_STR_LONG] );
    printf( " 連結リストから要素を削除する: %s (%s)\n", CMD_STR[CMD_DELETE][CMD_STR_SHORT], CMD_STR[CMD_DELETE][CMD_STR_LONG] );
    printf( " 連結リストから要素を探す: %s (%s)\n", CMD_STR[CMD_SEARCH][CMD_STR_SHORT], CMD_STR[CMD_SEARCH][CMD_STR_LONG] );
    printf( " 連結リストを空にする: %s (%s)\n", CMD_STR[CMD_CLEAR][CMD_STR_SHORT], CMD_STR[CMD_CLEAR][CMD_STR_LONG] );
    printf( " 連結リストの中身を出力する: %s (%s)\n", CMD_STR[CMD_PRINT][CMD_STR_SHORT], CMD_STR[CMD_PRINT][CMD_STR_LONG] );
    printf( " 終了する: %s(%s)\n", CMD_STR[CMD_EXIT][CMD_STR_SHORT], CMD_STR[CMD_EXIT][CMD_STR_LONG] );
    puts( "" );
}

// 空白行を出力
void print_blank_lines(void)
{
    puts( "" );
    puts( "" );
}

// コマンドを受け付ける
enum CmdRetValue_tag get_cmd(void)
{
    char buf[20];
    enum Cmd_tag cmd;
    int i;

    get_line( buf, sizeof(buf) );

    cmd = CMD_NUM;
    for( i = 0; i < CMD_NUM; ++i ){
        if( strcmp( buf, CMD_STR[i][CMD_STR_SHORT] ) == 0
         || strcmp( buf, CMD_STR[i][CMD_STR_LONG] ) == 0
        ){
            cmd = i;
            break;
        }
    }

    if( 0 <= cmd && cmd < CMD_NUM ){
        return CMD_FUNC[i]();
    }
    else{
        puts( "そのコマンドは存在しません。" );
    }

    return CMD_RET_VALUE_CONTINUE;
}

// addコマンドの実行
enum CmdRetValue_tag cmd_add(void)
{
    char buf[40];
    int value;

    puts( "追加する数値データを入力して下さい。" );
    fgets( buf, sizeof(buf), stdin );
    sscanf( buf, "%d", &value );

    add_elem( value );

    return CMD_RET_VALUE_CONTINUE;
}

// deleteコマンドの実行
enum CmdRetValue_tag cmd_delete(void)
{
    char buf[40];
    int value;

    puts( "削除する数値データを入力して下さい。" );
    fgets( buf, sizeof(buf), stdin );
    sscanf( buf, "%d", &value );

    if( delete_elem(value) >= 1 ){
        puts( "要素を削除しました。" );
    }
    else{
        puts( "削除する要素は見つかりませんでした。" );
    }

    return CMD_RET_VALUE_CONTINUE;
}

// searchコマンドの実行
enum CmdRetValue_tag cmd_search(void)
{
    char buf[40];
    int value;

    puts( "探索する数値データを入力して下さい。" );
    fgets( buf, sizeof(buf), stdin );
    sscanf( buf, "%d", &value );

    if( search_elem(value) == NULL ){
        printf( "%d は連結リスト中に存在しません。\n", value );
    }
    else{
        printf( "%d は連結リスト中に存在します。\n", value );
    }

    return CMD_RET_VALUE_CONTINUE;
}

// clearコマンドの実行
enum CmdRetValue_tag cmd_clear(void)
{
    clear_list();

    return CMD_RET_VALUE_CONTINUE;
}

// printコマンドの実行
enum CmdRetValue_tag cmd_print(void)
{
    print_list();

    return CMD_RET_VALUE_CONTINUE;
}

// exitコマンドの実行
enum CmdRetValue_tag cmd_exit(void)
{
    puts( "終了します。" );

    return CMD_RET_VALUE_EXIT;
}

// 要素を追加する
// 引数:
//        value:    追加する要素の数値データ。
void add_elem( int value)
{
    struct LinkedList_tag* elem;
    struct LinkedList_tag* tail;

    // リストの末尾を探す 
    tail = search_tail();

    // 追加する要素を作る 
    elem = malloc( sizeof(struct LinkedList_tag) );
    if( elem == NULL ){
        fputs( "メモリ割り当てに失敗しました。", stderr );
        exit( 1 );
    }
    elem->value = value;         
    elem->next = &gHead;  
    elem->prev = tail;     

    tail->next = elem;

}

// 要素を削除する
// 引数:
//        value:    削除する要素が持つ数値データ。
// 戻り値:
//        削除できた要素の個数を返す。
int delete_elem(int value)
{
    struct LinkedList_tag* p = gHead.next;
    int count = 0;

    while( p != &gHead ){
        if( p->value == value ){
            p = delete_one_node( p );
            // 削除後に有効なポインタを返す。
            // 削除した要素の次にあった要素へのポインタを返す。

            ++count;
        }
        else{
            p = p->next;
        }
    }

    return count;
}

// 要素を空にする
void clear_list(void)
{
    struct LinkedList_tag* p = gHead.next;

    while( p != &gHead){
        p = delete_one_node( p );

    }
}

// 要素を出力する
void print_list(void)
{
    struct LinkedList_tag* p = gHead.next;

    if( p == &gHead ){
        puts( "リストは空です。" );
        return;
    }

    while( p != &gHead ){
        printf( "%d\n", p->value );
        p = p->next;
    }
}

// 末尾の要素を探す
// 戻り値:
// 連結リストの末尾にある要素を指すポインタ。
struct LinkedList_tag* search_tail(void)
{
    struct LinkedList_tag*  p= &gHead;

    while( p->next != &gHead ){
        p = p->next;
    }
      return p;        
}

// 指定した値を持つ要素を探す
// 引数:
//        value:    探し出す要素が持つ数値データ。
// 戻り値:
//        先頭から連結リストを辿り、最初に見つけた value と同じ値を持つ要素のアドレス。
//        見つからなかった場合は NULL を返す。
struct LinkedList_tag* search_elem(int value)
{
    struct LinkedList_tag* p = gHead.next;

    while( p != &gHead ){
        if( p->value == value ){
            return p;
        }
        p = p->next;
    }

    return NULL;
}

// 1つの要素を削除する
// 引数:
//        node:    削除する要素を指すポインタ。
//    戻り値:
//        削除後に有効なポインタ。
struct LinkedList_tag* delete_one_node(struct LinkedList_tag* node)
{
        struct LinkedList_tag* const prev = node->prev;

        if( node->next != &gHead ){
        node->next->prev = prev;
      }
        prev->next = node->next;

        free( node );

        return prev->next;
}

void get_line(char* buf, size_t size)
{
    fgets(buf, size, stdin);

    // 末尾に改行文字があれば削除する 
    char* p = strchr(buf, '\n');

    if (p != NULL) {
        *p = '\0';
    }
}


 実行結果
naka@naka ~/kadai/kadai9-8
$ gcc -g LST_sou_jyun21.c   -o LST_sou_jyun21   -Wall

naka@naka ~/kadai/kadai9-8
$  gdb LST_sou_jyun21

省略...

(gdb) p i
$12 = 0
(gdb) p cmd
$13 = CMD_ADD
(gdb) step
171             if( 0 <= cmd && cmd < CMD_NUM ){
(gdb) step
172                     return CMD_FUNC[i]();
(gdb) p  CMD_FUNC[i]()
追加する数値データを入力して下さい。
6

Program received signal SIGSEGV, Segmentation fault.
0x00401936 in search_tail () at LST_sou_jyun21.c:347
347             while( p->next != &gHead ){
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(cmd_add) will be abandoned.
When the function is done executing, GDB will silently stop.
(gdb) step
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 4

+1

エラーメッセージを読んでいますか?

Program received signal SIGSEGV, Segmentation fault.
0x00401936 in search_tail () at LST_sou_jyun21.c:347
347             while( p->next != &gHead ){

ここでエラーだと言っています。

初期化のせいで
p->next != &gHead
この条件文がfalseになるときが来ないと思います。
エラー時、p == NULL になっていませんか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

void init_head(void) {

    gHead.value = 0;
    gHead.next    =NULL; 
    gHead.prev    =NULL;    
}

なるほど、初期状態では次/前の要素は無い(NULL)のだな。

では追加してみよう。
追加時にはまずsearch_tailで末尾を見つけてその次に繋ぐのか。
search_tailでは:

struct LinkedList_tag* search_tail(void) {
    struct LinkedList_tag*  p= &gHead;

    while( p->next != &gHead ){
        p = p->next;
    }
      return p;        
}

...ちょっとマテ。初期状態では p->next は NULL ではなかったか?
これでは while-loop から抜けられんではないか!

ってわけでinit_headまたはsearch_tailが悪い。最悪両方悪い。

だからさー、闇雲にいぢくり回しても動くようにはならんのだよ。
理詰めで実装しないと。
そのためにさんざん図を描いて(もらって)たんじゃないのか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

こんにちは。

// 末尾の要素を探す
// 戻り値:
// 連結リストの末尾にある要素を指すポインタ。
struct LinkedList_tag* search_tail(void)
{
    struct LinkedList_tag*  p= &gHead;

    while( p->next != &gHead ){
        p = p->next;
    }
      return p;        
}


セグフォルトで落ちているのは上記のwhile文ですね。

pは最初にgHeadを指してます。
init_head()でgHead.next=NULL;していますから、p->nextはNULLです。
&gHeadとは等しくないので次のp = p->next;が実行され、pはNULLになります。
その後、再度( p->next != &gHead )を比較しようとしてpがNULLなので不正メモリ・アクセスとなり落ちてます。
while( p->next != NULL )の間違いではないでしょうか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

check解決した方法

0

void init_head(void)
{

gHead.value = 0;
gHead.next    =NULL;  //NULLから&dummyに変更
gHead.prev    =NULL;  //NULLから&dummyに変更 
}

int main()の前に
static struct LinkedList_tag dummy;
static struct LinkedList_tag* gHead = &dummy;    
追加する。    

以上で正常動作 しました。
epistemeさん、Chironianさん、それからご指導いただいたみなさんありがとうございました。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/05 12:21 編集

    循環双方向リストであるなら、search_tailで末尾が見つかるまでloopしているのは無駄じゃないのか?
    無駄だろうが遅かろうが動けばOKなら構わんが、だったらなんで循環双方向リストみたいなややこしいの使ったんだ? 単方向リストで十分ではないか。

    キャンセル

  • 2018/03/05 14:46

    この際リストの種類を知っておきたいと思いました。まだまだ知識不足ですので。
    工夫してコードが書けるれべるではありません。すこしずつおぼえるのみです。
    それではまた。電話帳に帰ります。

    キャンセル

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

  • ただいまの回答率 89.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • Cに関する質問
  • プログラムをコンパイルして、コマンドを実行できるのですが、数値を打ち込んだ時点で、止まります。