プロが教える店舗&オフィスのセキュリティ対策術

C言語の勉強をしております。
双方向リストへのデータの登録でうまくいかず詰まってしまいました。


<ヘッダファイル>
// 構造体
typedef struct address {
char names[32];/* 名前 */
char tels[32];/* 電話番号 */
struct address *prev;/* 前へのポインタ */
struct address *next;/* 次へのポインタ */
}Address;

extern Address *top_pt;
extern Address *work_pt;
extern Address *w_pt;
extern Address *inAdd;




// ファイルからの読み込み

top_pt = NULL;
while(!feof(fp)) {
work_pt = (Address *)malloc(sizeof(Address));
if(!work_pt){
printf("メモリ不足");
exit(1);
}
fscanf(fp, "%s %s", work_pt->tels, work_pt->names);
work_pt->next = top_pt;
top_pt = work_pt;
work_pt->prev = top_pt;
/* カウンタを用意して、実際のデータ件数をカウントしておく */
++trueCount;

}


これで、双方向リストへファイルの内容を格納することは出来ました。

次に、構造体へデータを新規登録したいのですが、構造体を共有している別ソースファイルにて、

for(i=0; 1000>trueCount+i; i++){
inAdd = (Address *)malloc(sizeof(Address));
if(!inAdd){
printf("メモリ不足");
exit(1);
}
printf("登録する名前を入力してください:");
gets(inAdd->names);
/* 名前が未入力でEnterされた場合 */
if(!*inAdd->names){
/* ループを抜け、トップメニューへ */
flag = 1;
break;
}
printf("電話番号を入力してください:");
scanf("%s", inAdd->tels);

/* 読み込んだ構造体へデータを追加する */
work_pt->next = inAdd;
inAdd->prev = work_pt;
inAdd->next = NULL;
}


として、構造体の中身を表示させたところ、
ファイルの最終データと、入力したデータのみになってしまいました。

なぜなのか分かりません・・・。

有識者の方でこの下手なソースを読んで指南していただける方はおられますでしょうか?

また、データ登録の際に名前の入力に「gets」を使用しているんですが、
「scanf」だとその次のif文が効かないみたいで少し困っています。
(出来ればANSIC標準のscanfを使いたい)

以上、よろしくお願いいたします。

A 回答 (9件)

アドバイスの続きです。



>>●本当は、どのような動作を期待していましたか?
>ファイルから読み込んだデータに、入力したデータが追加されていくイメージです。

> データの入力を
> top_pt->next = inAdd;
> inAdd->prev = top_pt;
> inAdd->next = NULL;
> のようにしてもうまくいかなかったんですが、これもおかしいでしょうか?
・リストの先頭に要素を追加するなら、
inAdd->next = top_pt;/* 入力データのnextに今の先頭要素をリンク。 */
inAdd->prev = NULL;/* 入力データのprevにNULLを代入 */
top_pt->prev = inAdd;/* 先頭要素のprevに入力データをリンク。 */
top_pt = inAdd;/* 入力データを先頭要素とする */
で良いと思います。

・リストの末尾に要素を追加したい場合、
top_pt以外に、bottom_pt等を用意して、最終要素も管理してあげると良いのでは、
ないでしょうか。

・蛇足
アルゴリズムが理解できたら、次はもう一歩発展させて、要素に追加する処理を
関数化してみてはどうでしょう。
例えば、
/**
* 双方向リストの先頭にアイテムを追加する。
*
* @param[in] pItem 追加する要素
*/
int insertItemToTop(Address *pItem) {
[づらづら]
}

・蛇足その2
> また、ファイルの末尾のチェック方法についてですが、ループの途中に、fscanfとEOFを比較して、ループから抜けるようにしました。
>
> ごみデータが入ってしまう事は私も分かっており、悩んでいたんですが、回答者様のご指摘を参考に上記処理にて解決できました。(正しい方法なのかは分かりませんが・・・)
ファイル読込にも、fgets() + sscanf()の組み合わせが有効な場合もあるかもしれません。
    • good
    • 0
この回答へのお礼

アドバイスありがとうございます。

関数化について考えてみたいと思います。

また、fgetsとsscanの組み合わせの有効的な使用方法は分かりませんので、とりあえず私の分かる範囲で処理を記述してみたいと思います。

色々アドバイスありがとうございました。

お礼日時:2008/10/29 14:30

標準に含まれているかどうかは, それこそ「標準を確認する」だけでわかるはずです. ANSI そのものには当たっていませんが, ISO や JIS の規格を調べれば「gets が標準に含まれている」ことは想像できるはずです. JIS なら


http://www.jisc.go.jp/
の「JIS 検索」で規格番号として「X3010」を入れれば閲覧だけは出来ます.
あと, この書き方では「最後にごみデータが入る」可能性があります. 「ファイルの末尾」のチェック方法によるもので, 実際に空のファイルを読み込ませてみれば簡単にわかるはず.
    • good
    • 0
この回答へのお礼

ありがとうございます。

また、ファイルの末尾のチェック方法についてですが、ループの途中に、fscanfとEOFを比較して、ループから抜けるようにしました。

ごみデータが入ってしまう事は私も分かっており、悩んでいたんですが、回答者様のご指摘を参考に上記処理にて解決できました。(正しい方法なのかは分かりませんが・・・)

ありがとうございました。

お礼日時:2008/10/29 13:20

ああ, ついでにもう 1つですが, この


「ファイルからの読み込み」
のところは論理的にたぶん間違っています. この形で feof を使うのは危ない気がするなぁ.
    • good
    • 0

すみません,


「『scanf』はANSIC標準だけど、『gets』は違う」
という情報はどこにありますか?
もちろん「gets は危険なので使うべきではない」というのはその通りだけど....
    • good
    • 0
この回答へのお礼

カーニハン/リッチーのANSIC規格準拠のプログラミング言語Cに「gets」関数が出てこないので、標準ではないのかと思っていますが、違いましたでしょうか?

また、No7についてですが、理由が全く分かりません(泣)
何故なのか教えていただけないでしょうか?

お礼日時:2008/10/29 11:21

●これは何をしめしていますか?


extern Address *top_pt;
extern Address *work_pt;
extern Address *w_pt;
extern Address *inAdd;

#推測
extern Address *top_pt;// 双方向リストの先頭要素
extern Address *work_pt;// 処理中データをしめすワーク
extern Address *w_pt;// 何??
extern Address *inAdd;// 入力中のデータ

> これで、双方向リストへファイルの内容を格納することは出来ました。
双方向リストの前方に、1件づつデータを追加していくアルゴリズムみたいですね。

> /* 読み込んだ構造体へデータを追加する */
> work_pt->next = inAdd;
> inAdd->prev = work_pt;
> inAdd->next = NULL;
この時点で work_pt はどこをさしているつもりですか?
私の予想だと work_pt は先頭要素ですね。


> work_pt->next = inAdd;
で、先頭要素のnextに入力データをリンク。

> inAdd->prev = work_pt;
入力データのprevに先頭要素をリンク。

> inAdd->next = NULL;
入力データのnextにNULLを代入。

しているので、

> として、構造体の中身を表示させたところ、
> ファイルの最終データと、入力したデータのみになってしまいました。
そのとおりの結果なのではないでしょうか?

●本当は、どのような動作を期待していましたか?

> また、データ登録の際に名前の入力に「gets」を使用しているんですが、
> 「scanf」だとその次のif文が効かないみたいで少し困っています。
> (出来ればANSIC標準のscanfを使いたい)
未入力でEnterが押された場合、scanfが再度入力待ちになるのは、正しい動作です。
未入力でEnterが押された場合を判定して、処理したい場合、
拙者なら、
----------------------------------------------------------------------
    char buffer[4096];
    
    memset(buffer, 0, sizeof(buffer));
    fgets(buffer, sizeof(buffer) - 1, stdin);
    if (buffer[0] == '\n') {
      /* ループを抜け、トップメニューへ */
      flag = 1;
      break;
    }
    sscanf(buffer, "%s", inAdd->names);
----------------------------------------------------------------------
のような処理にしてしまいますが、どうでしょう。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

>extern Address *w_pt;// 何??
ですが、mallocで確保したメモリの解放時に使用しているので、ここでは不必要でした。
必要の無いものまで一緒に貼り付けてしまい申し訳ございませんでした。

>●本当は、どのような動作を期待していましたか?
ファイルから読み込んだデータに、入力したデータが追加されていくイメージです。

データの入力を
top_pt->next = inAdd;
inAdd->prev = top_pt;
inAdd->next = NULL;
のようにしてもうまくいかなかったんですが、これもおかしいでしょうか?

>のような処理にしてしまいますが、どうでしょう。
参考にさせていただきます。
本当にありがとうございました。

お礼日時:2008/10/29 10:14

> 「scanf」だとその次のif文が効かないみたいで少し困っています。


> if(!*inAdd->names){

* は、どういった意味合いで付けていますか?
    • good
    • 0
この回答へのお礼

「構造体のデータ」です。

お礼日時:2008/10/29 10:04

すみません, できれば「出来れば ANSI C 標準の scanf を使いたい」の意味をお教えいただけますか? ひょっとして, 「

ANSI C 標準でない scanf」もある?
    • good
    • 0
この回答へのお礼

「scanf」はANSIC標準だけど、「gets」は違うという意味で書いただけです。

お礼日時:2008/10/29 10:03

せっかくソースコードを載せるのであれば、


回答者のところで「そのまま」コピー&ペーストして
コンパイル&実行できるよう、
断片でなく全体を載せる方がよいと思いませんか?
載っていない部分を想像で補うことができないのです。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

全体が大きかったので、必要かなと思う箇所のみ貼り付けておりました。
申し訳ございません。

お礼日時:2008/10/29 10:02

とりあえずヒントだけ。



>構造体の中身を表示させたところ、
>ファイルの最終データと、入力したデータのみになってしまいました。
>なぜなのか分かりません・・・。
ファイルの読み込みと、キー入力でリストの接続処理が違う理由は何でしょうか?
入力がファイルかキーボードかが違うだけでやることは一緒ですよ。

>また、データ登録の際に名前の入力に「gets」を使用しているんですが、
>「scanf」だとその次のif文が効かないみたいで少し困っています。
scanf()で取り込まれている内容がどうなっているのか、確認してみましょう。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

お礼日時:2008/10/29 10:01

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!