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

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

ただいまの
回答率

89.55%

c言語 線形リストと動的領域確保(malloc関数)について

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 591

_ta_ryo

score 5

前提・実現したいこと

C言語で線形リストを用いて講義の科目名、単位数、成績をリストに保存できるプログラムを作成しています。
コンパイルは問題なく通るのですが実行すると上手く動かないため質問させていただきます。

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

実行後、1を入力してデータを追加すると途中で終了してしまいます(以下が実行結果)

C:\Users\tar\program>a
1:科目の追加    2:科目の挿入    3:要素の削除
4:要素の表示    5:GPAの表示     0:終了
操作を入力してください:1
科目名を入力してください:w
単位数を入力してください:1
GPを入力してください2

aa

ただしスイッチ文のcase1の中のprintf内の文章を長めの文字(ここでは0000000)に変更すると以下のように実行できます。

C:\Users\tar\program>a
1:科目の追加    2:科目の挿入    3:要素の削除
4:要素の表示    5:GPAの表示     0:終了
操作を入力してください:1
科目名を入力してください:w
単位数を入力してください:1
GPを入力してください2
0000000
aaii1:科目の追加        2:科目の挿入    3:要素の削除
4:要素の表示    5:GPAの表示     0:終了
操作を入力してください:4
length = 1
w       1       2.00
1:科目の追加    2:科目の挿入    3:要素の削除
4:要素の表示    5:GPAの表示     0:終了
操作を入力してください:0

aaまで表示できることからmallocの使い方が悪いと思うのですが、どのように改善したらよいか分かりません。
今回の質問に関係のないremovedata関数やinsertdata関数は省略させていただきました。
ご教示よろしくお願いいたします。

該当のソースコード

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

/* 要素データの構造定義 */
struct Data {
  char *name;
  int num;
  double gp;
  struct Data *pNext;
};
typedef struct Data data;

/* リストの構造定義 */
typedef struct {
  data *pHead;
  int length;
} List;

/* 関数プロトタイプ */
void initialize(List*);
void disp(List);
void adddata(List*, char*, int, double);
void removedata(List*, int);
void insertdata(List*, int, char*, int, double);

int main() {
    char str[30];
    int num, n, mode;
    double gp;

    /* リスト型の宣言 */
    List v;

    /* 初期化 */
    initialize(&v);

    do{
        printf("1:科目の追加\t2:科目の挿入\t3:要素の削除\n");
        printf("4:要素の表\示\t5:GPAの表\示\t0:終了\n");
        printf("操作を入力してください:");
        scanf("%d", &mode);


        switch(mode){
            case 1 : printf("科目名を入力してください:");   scanf("%s",str);
                     printf("単位数を入力してください:");   scanf("%d",&num);
                     printf("GPを入力してください");        scanf("%lf", &gp);
                     printf("\n");
                     adddata(&v, str, num, gp);
                     break;
            case 2 : printf("科目名を入力してください:");   scanf("%s",str);
                     printf("単位数を入力してください:");   scanf("%d",&num);
                     printf("GPを入力してください");        scanf("%lf", &gp);
                     printf("何番目の前に挿入しますか:");   scanf("%d",&n);
                     printf("\n");
                     insertdata(&v, n-1, str, num, gp);
                     break;
            case 3 : printf("何番目を削除しますか:");
                     scanf("%d", &n);
                     removedata(&v, n);
                     break;
            case 4 : printf("length = %d\n", v.length);
                     disp(v);
                     break;
            case 0 : break;         
            default: printf("正しい値を入力してください\n");
                     break;
        }


    }while(mode != 0);

    /* 全要素の削除 */
    while(v.pHead == NULL){
        removedata(&v, 1);
    }

    return 0;
}

/* 線形リストの初期化 */
void initialize(List *pV) {  
    pV->pHead = NULL;
    pV->length = 0;
}

/* 要素の表示 */
void disp(List v) {
    data *pd;
    pd = v.pHead;

    if(pd == NULL)
        printf("リストには何もありません。\n");
    else {
        while(pd != NULL) {
            printf("%s\t%d\t%1.2f\n", pd->name,pd->num,pd->gp);
            pd = pd->pNext;
        }
    }
}

/* 要素の追加 */
void adddata(List *pV, char *name, int num, double gp) {

    /* 最後尾要素のポインタ */
    data *pLast;

    /* 新規に追加する要素の生成 */
    data *pNew;
    printf("aa");
    pNew->name = (char*)malloc(sizeof(name));
    printf("aaaa");
    pNew = (data*)malloc(sizeof(data));/* 領域確保 */
    pNew->name = name;
    pNew->num = num;
    pNew->gp = gp;
    pNew->pNext = NULL;

    if( pV->pHead == NULL ) { /* リストが空の場合 */
        pV->pHead = pNew; /* 新規要素を先頭にリンク */
    } else { /* リストが空でない場合 */
        pLast = pV->pHead;
        while( pLast->pNext != NULL ) /* 最後尾を探索 */
            pLast = pLast->pNext;
        pLast->pNext = pNew; /* 新規要素を最後尾にリンク */
    }
    pV->length++;
}

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

OS:windows10
コンパイラ:gcc

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • y_waiwai

    2019/06/03 17:35

    このままではコードが見づらいので、質門を編集し、<code>ボタンで、出てくる’’’の枠の中にコードを貼り付けてください

    キャンセル

  • _ta_ryo

    2019/06/03 18:00

    ご指摘ありがとうございます。修正させていただきました。

    キャンセル

回答 3

+2

    /* 新規に追加する要素の生成 */
    data *pNew;
    pNew = (data*)malloc(sizeof(data));/* 領域確保 */
    pNew->name = (char*)malloc(strlen(name)+1); /* 文字列格納するのに十分な領域を確保し*/
    strcpy(pNew->name, name);                   /* しかるのちそこにコピー */
    pNew->num = num;
    pNew->gp = gp;
    pNew->pNext = NULL;


...ぢゃろね。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

checkベストアンサー

+1

pNew->name = (char*)malloc(sizeof(name));
printf("aaaa");
pNew = (data*)malloc(sizeof(data));/* 領域確保 */
pNew->name = name;

ちと分かりづらいので修正
nameはなんでしょう。
そして、pNew->name には何が入るんでしょう

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/06/03 18:03 編集

    回答ありがとうございます。
    pNew->nameにはadddata関数の引数であるnameが入ります。
    構造体pNewの中のnameの領域確保→構造体のnameに引数であるnameを入れるという流れを行いたいと考えました。
    変数名を同じにしてしまったためややこしくなってしましました…

    キャンセル

  • 2019/06/03 18:07

    まず、この最初の行はアクセス違反となります
    pNew は初期化されてないので、でたらめな値が入ってます。
    運が良ければ暴走しますが、運が悪けれ動き続けます。

    そして、ここのnameという値は、なんでしょうか。どこからくる値?

    キャンセル

  • 2019/06/03 18:30

    pNewのメンバであるnameはポインタ変数なので、領域確保をした後でないと初期化も別の変数を保存することもできないと考えたのですが、そもそもpNewという構造体そのものを初期化した後でないとpNewのメンバにアクセスして領域確保も行えないということでしょうか?
    その場合、構造体の初期化とはどのようにして行うのでしょうか?
    callocを利用することも考えたのですが、pNewのメンバであるnameのサイズが未決定であるため構造体のサイズも定まらないと考えたのですが、そのような事はないのでしょうか?

    nameはメイン関数内でscanf関数によって入力されたstrをadddata関数の引数として持ってきたもので、adddata関数内での変数名をnameとしています。

    キャンセル

  • 2019/06/03 18:48

    pNewってのは、dataのポインタとして定義されてます
    本来、どこかにあるdataのアドレスが入るべきものです
    ところが、その何処かにあるdataのアドレスを入れる前にそのメンバに何かを入れてます。

    そしてべつのはなし
    nameという変数はどこから来ているかというと、main関数内で定義されたstrのアドレスですね。
    main関数内でいくらループしていようが、このstrという配列のアドレスは変わるもんじゃありません。
    つまりは、生成されたdataのnameはすべて同じ値が入ります

    char*  といいうのはあくまでアドレスが入ります。文字列というのは、そのアドレスの指した先に格納されてます。

    キャンセル

0

y_waiwaiさん、epistemeさんの回答の要約ですが…

お二人の指摘するように、このプログラムには問題が3つあります。

  1. (未初期化のポインタ)pNewの内容にアクセスしてメンバ変数nameを初期化しようとしていること。得体の知れない領域を書き換えているので何がおきてもおかしくありません。

  2. nameメンバの初期化。文字列のコピーには文字列の文字数+1(ヌル文字)個以上の要素からなるchar型の配列が必要です。sizeof演算子で引数の文字列の大きさを求めようとしていますが、この方法では上手くいきません。なぜなら、引数で渡されるのはあくまで配列の先頭要素へのポインタで、sizeofはそのポインタのバイト数を取得する仕様になっているからです。文字列の文字数を取得する場合、strlen関数を使う必要があります。

  3. nameメンバの内容の初期化。今の方法ではnameメンバ(ポインタ)を引数のname(ポインタ)で初期化しています。ポインタの先の内容をコピーする場合、strcpy関数を使う必要があります。

以上、要約です。問題点を簡潔にまとめただけなので、厳密には誤っている部分もあります。不明な点は一度自分で要約中からキーワードを拾って検索し、それで分からない場合はコメントを送ってください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

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