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

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

ただいまの
回答率

90.50%

  • C

    4543questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

住所録のプログラムのあちこち分からないところがあります。教えてください。

解決済

回答 2

投稿

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

sanchu52

score 159

下記の住所録プログラムについて分からないところを質問させてください。
質問1:if (1 <= i && i <= 5) cnt = func[i - 1](ad, cnt);
質問2:while (j++ < cnt){}条件がよくわかりません
質問3:for (j = i - 1; j < cnt - 1; j++)
ap[j]= ap[j + 1];がいまいちわかりません。
よろしくお願いします。

コード
// アドレス帳 住所録プログラム
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// プログラム中で多用する配列の大きさ 
#define BUFSIZE 128
#define DATASIZE 100

// 構造体の宣言 -- typedefを使用して以後srtuctを省略できるようにした 
typedef struct address {
  char szAdd[51];   // 住所 
  char szName[21];  // 氏名 
  char szTel[21];   // 電話番号
} Address;            

// 関数のプロトタイプ宣言 -- ジャンプテーブルに入れるため引数及び返却値型は
// すべて同じとした 
int search(Address *ap, int cnt);
int add(Address *ap, int cnt);
int amend(Address *ap, int cnt);
int del(Address *ap, int cnt);
int printall(Address *ap, int cnt);

// 関数のジャンプテーブル 
int (*func[])(Address *, int) = {search, add, amend, del, printall};
// 処理を番号で選ばせ、所定の関数に飛ばす
// 処理によっては配列の要素数が変化する事があるので常に要素数 cnt を返させる

// 構造体の配列 -- 大きくてスタックを消費する可能性があるので静的に定義した 
Address ad[DATASIZE];

int main(void)
{
  char dummy[BUFSIZE];
  int i, cnt = 0;

  while (1) {
    puts("●メニューを入力して下さい");
    puts("1.検索\n2.追加\n3.修正\n4.削除\n5.全体表\示\n6.終了");
    scanf("%d", &i); 
    gets(dummy);

    if (1 <= i && i <= 5) cnt = func[i - 1](ad, cnt);
    //cntは要素番号

    else if (i == 6) break;
  }

  return 0;
}

// 検索するデータをswitch文によって処理を選択
int search(Address *ap, int cnt)
{
  char buf[BUFSIZE], dummy[BUFSIZE];
  char *target[] = {"住所", "氏名", "電話番号"};
  int i, j = 0, count = 0;
  Address *t = ap;

  while (1) {
    puts("何で探しますか?\n1.住所 2.氏名 3.電話番号");
    scanf("%d", &i); 
    gets(dummy);
    //scanfは変換が終わったら入力の残りをそのままにします。
    //つまり、改行が内部バッファーに残り、これがgetsに渡ってしまいます。
    //getsはこれを受け取り、新たなキー入力を待たずに終了してしまいます。
    //一番簡単な修正はscanf後にfgetsでダミーで一行を読み込むことでしょう。

    if (1 <= i && i <= 3) break;
  }
  printf("%sを入力して下さい:", target[--i]);
  gets(buf);

  while (j++ < cnt) {
    switch (i) {
    case 0: // 住所 
      if (!strcmp(t->szAdd, buf)) goto print;
      break;
    case 1: // 氏名 
      if (!strcmp(t->szName, buf)) goto print;
      break;
    case 2: // 電話番号 
      if (!strcmp(t->szTel, buf)) goto print;
      break;
    default: // no condition、ここには来ない 
      break;
    }
    t++;
    continue;
print:;
    printf("%d: 住所:%s 氏名:%s 電話番号:%s\n", j, t->szAdd, t->szName, t->szTel);
    t++;
    count++;
  }
  printf("%d件見つかりました\n", count);

  return cnt;
}
// 構造体の*最後*の次に新しいデータを付け加える
// もし既に構造体の配列が一杯になっていたら警告を発して戻る
// 追加に成功したら構造体の要素数が 1 増えるので cnt + 1 を返す
int add(Address *ap, int cnt)
{
  Address *t = ap + cnt;    // 配列の最後の次のアドレスの計算 、配列の最後の要素の
                            // 次のアドレスを指してもよい 

  if (cnt == DATASIZE) {
    puts("これ以上追加できません");
    return cnt;
  }
  printf("住所:"); // 住所 
  gets(t->szAdd);

  printf("氏名:"); // 氏名 
  gets(t->szName);

  printf("電話番号:"); // 電話番号 
  gets(t->szTel);
  // 表示
  // printf("%s %s %s\n", t->szAdd, t->szName, t->szTel); 

  return cnt + 1;
}

// 任意の構造体の内容を修正する
// 修正しない要素はreturnで飛ばせるようにした
int amend(Address *ap, int cnt)
{
  Address *t;
  char buf[BUFSIZE];
  int i;

  puts("何番を修正しますか?");
  scanf("%d", &i); 
  gets(buf);

  if (i > 0 && i <= cnt) {
    t = ap + i - 1;             // 配列のアドレスの計算 

    puts("変更しない場合は単に[return]を押して下さい");

    printf("住所: %s -> ", t->szAdd);
    gets(buf);
    if (strlen(buf)) strcpy(t->szAdd, buf);

    printf("氏名: %s -> ", t->szName);
    gets(buf);
    if (strlen(buf)) strcpy(t->szName, buf);

    printf("電話番号: %s -> ", t->szTel);
    gets(buf);
    if (strlen(buf)) strcpy(t->szTel, buf);

  } else 
    puts("番号が不適切です");
  return cnt;
}
// 任意の構造体を削除する
// 削除方法はそれより後ろの構造体を一つ前にずらし cnt - 1 を返す
// i == cnt の時は実際には cnt - 1 を返すだけである
int del(Address *ap, int cnt)
{
  char buf[BUFSIZE];
  int i, j;

  puts("何番を削除しますか?");
  scanf("%d", &i); 
  gets(buf);

  if (i > 0 && i <= cnt) {
    for (j = i - 1; j < cnt - 1; j++)
      ap[j]= ap[j + 1];
    return cnt - 1;
  }
  else {
      puts("番号が不適切です");
      return cnt;
  }
}

// 格納された構造体を全部表示する
// 構造体には番号が付いてないので、カウンタ i で内部的に処理する(構造体の添え字i + 1)
int printall(Address *ap, int cnt)
{
  int i = 0;

  while (i < cnt) {
    printf("%d: 住所:%s 氏名:%s 電話番号:%s\n", ++i, ap->szAdd, ap->szName, ap->szTel);
    ap++; // 配列へのポインタが次の配列要素を指すようにする 
  }

  return cnt;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

質問1:if (1 <= i && i <= 5) cnt = func[i - 1](ad, cnt);

関数へのポインターは5要素あり、iはそのどの要素の関数かを示すインデックス(ただしCで普通にもちいるような0始まりのインデックスではなくなぜか1始まりとしているようです)で、
func[i-1]はi番目の要素の関数ポインターfunc[i-1](ad, cnt)はその関数ポインターが指す関数を、adとcntを引数にして呼び出すコードです。

質問2:while (j++ < cnt){}条件がよくわかりません

このプログラムは誰が書いたか知りませんが・・・これは本来

for (int j = 0; j < cnt; j++) {
  ...
}


と書くべきように思えます。個人的な意見としてはひどいコードです。jが0で初期化されてるのは関数の先頭の変数宣言ですね。そもそもjはこのループ内部でしか参照されていないように見えますので「なぜ関数の先頭で宣言しなけりゃいけないの?」と思ってしまいます。もし勉強のためのこのコードを見ているならあえて言わせていただければ「やめた方がよい」です。

質問3:for (j = i - 1; j < cnt - 1; j++)
ap[j]= ap[j + 1];がいまいちわかりません。

これは配列の各要素を順番に先頭方向へひとつずつずらしているコードです。これがわからないとするとループについてご自分でプログラムを書いて経験を積むべきと思います。また質問の際に「いまいちわからない」という表現は禁句と思ってください。閲覧者にも「あなたに何がわからないのかイマイチわかりません」わからないことを具体的に書いてください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/12 19:24

    そうですね。少し疲れていました。休憩して1つ1つ分からないところをつぶしていきたいとおもいます。
    ありがとうございました。

    キャンセル

  • 2017/11/12 19:48

    根をつめすぎると・・・ということもあるかもです。地道にすすめてみてくださいね。

    キャンセル

  • 2017/11/12 20:01

    悪いコードの指摘のプログラムは削除して、明日からよみなおします。
    回答していただいた、ものはプログラムに貼り付けて参考にしていきたいとおもいます。ありがとうございます。ボケ防止にやっています。
    なかなか読み込みが悪くて、ごめんなさい。あまり長くやっていると余計おかしくなりますね。またよろしくおねがいいたします。

    キャンセル

  • 2017/11/12 20:12

    プログラミングは非常に広範囲の知識や経験がきいてくるものなので何もかも一挙にわかることはまずないと思います。地道にと申し上げたのはあわててもしかたないという考えがあるからです。自分は、ん十年続けてますがまだまだ初心者的な感じで永遠にベテランにはなれそうにないですが、アマチュアなので小さなことでもいいので何かしら面白さをみつけて楽しめるのが一番と思っています。

    キャンセル

+1

質問1:if (1 <= i && i <= 5) cnt = func[i - 1](ad, cnt);

入力値が不正でないかチェックして、ジャンプテーブルの要素を参照・実行しています。


質問2:while (j++ < cnt){}条件がよくわかりません

後置インクリメントです。条件式の評価後、jの値をインクリメントします。
個人的には、ただのfor文を使えばいいような気もします。

#include <stdio.h>

int main(void) {
    int j = 0;
    for(int i = 0; i < 10; i++) {
        printf("%d", j++);
    }
    printf("\n");
    return 0;
}

実行すると、『0123456789』が出力される。


質問3:for (j = i - 1; j < cnt - 1; j++)

i-1番目以降の要素を参照しています。
前後のコードを見る限り、要素の削除に伴って値を詰めているようです。


動作がわからないのか、構文がわからないのかがちょっとはっきりしない印象です。

  • 前者ならば、何らかの方法で動作を追ってみてください。
    デバッガを使えるならそれがよいでしょうし、標準出力を使ってもよいでしょう。
  • 後者ならば、参考書を確認してください。
    そして自身の理解が正しいのか、簡単にコードで試せるとよいでしょう。

参考にしているコードも、ちょっと怪しいかなぁって気がしますね。
何食わぬ顔でgoto文使ってますし...

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/12 19:17

    ありがとうございます。もう少し自分で見てみます。色々分からないところがあって、1日質問していて疲れました。明日少しやってみます。

    キャンセル

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

  • C

    4543questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • トップ
  • Cに関する質問
  • 住所録のプログラムのあちこち分からないところがあります。教えてください。