プロが教えるわが家の防犯対策術!

以下のようなプログラムを書いたのですが、うまく実行することができません。
データを複数個入力し、いつでも変更できる状態で調べたいもののみを抽出させた一覧も作りたいです。どのようにすれば実行できるのでしょうか。分かる方教えて頂きたいです。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Data{
char *namae;
char *sei;
long bangou;
}DATE;
int main(void){
DATE *x;
int menu;
menu=1;
int a;
a=1;
int y,z;
y=1;
while(a!=9){
if(menu==1){
printf("\n1.データを入力又は、変更\n");
printf("2.名前一覧\n");
printf("3.性別一覧\n");
printf("4.電話番号一覧\n");
printf("9.終了\n");
printf(">");
scanf("%d",&a);
}
switch(a){
case 1:
printf("何番目のデータを入力したいですか:");
scanf("%d",&y);
printf("名前を入力:");
scanf("%s",&x[y].namae);
x[y].namae=(char*)malloc(sizeof(char)*(strlen(&x[y].namae)+1));
printf("性別を入力:");
scanf("%s",&x[y].sei);
x[y].sei=(char*)malloc(sizeof(char)*(strlen(&x[y].sei)+1));
printf("電話番号を入力:");
scanf("%ld",&x[y].bangou);
x[y].bangou=(char*)malloc(sizeof(char)*(strlen(&x[y].bangou)+1));
break;
case 2:
for(z=1;z<=y;z++){
printf("%s",x[z].namae);
}
break;
case 3:
for(z=1;z<=y;z++){
printf("%s",x[z].sei);
}
break;
case 4:
for(z=1;z<=y;z++){
printf("%ld",x[z].bangou);
}
break;
case 9:
printf("\n終了します\n");
break;
}
}
}

A 回答 (7件)

うーん、まずは・・・。



> DATE *x;

これだけ、じゃダメですね。
ポインタ宣言してますが、これだけだと複数のデータ(構造体を)格納出来るメモリを確保出来ません。
多分意図してるのは構造体の配列が欲しい、ってトコだと思いますが、残念ながらC言語だと、最初に配列の大きさを宣言しとかないとならないか、あるいはメモリアロケーション使って適当なサイズを得ておかないとなりません。
ポインタだけ宣言しておいて、「データを追加する」みたいなのは他の言語じゃ可能ですが(例えば空リストを作っておいて順次データを追加していく、みたいな)、生憎Cはそういう融通が利きません。
どれだけ大きい配列を作るのか、ってのは貴方の好みですが、いずれにせよ、サイズは明確にしておかないとならないのです。

>printf("名前を入力:");
>scanf("%s",&x[y].namae);
>x[y].namae=(char*)malloc(sizeof(char)*(strlen(&x[y].namae)+1));
>printf("性別を入力:");
>scanf("%s",&x[y].sei);
>x[y].sei=(char*)malloc(sizeof(char)*(strlen(&x[y].sei)+1));
>printf("電話番号を入力:");
>scanf("%ld",&x[y].bangou);
>x[y].bangou=(char*)malloc(sizeof(char)*(strlen(&x[y].bangou)+1));

scanfで無理矢理構造体のメンバに代入しようとしてますが、これだと上手くいかないでしょう。
個人的には最初に別に文字配列格納の為の変数を用意しておいて、

> char s[12];

scanfを使って文字列を読み込み、

> scanf("%11s%*[^\n]%*c", s);

構造体のメンバの該当項目のメモリを確保して、

> x[y].namae = malloc(strlen(s));

strcpyを使ってsを該当のメンバにコピーします。

> strcpy(x[y].namae, s);

多分これで上手く行くんじゃないかな・・・・・・?

あと、多分、電話番号はlongじゃなくって、やっぱchar*にしておいた方が良いと思いますよ。
と言うのも、電話番号は大体0から始まってるんで、intとかlongだとアタマの0が消えちゃうから、ですね。
    • good
    • 0
この回答へのお礼

なるほど!scanfで文字列を読み取っているときのscanf(%11s%*[^\n]%*c)の部分は何を表しているのでしょうか?
もしよろしければ教えて頂きたいです。

お礼日時:2020/12/24 12:08

> scanfで文字列を読み取っているときのscanf(%11s%*[^\n]%*c)の部分は何を表しているのでしょうか?



次のページを参考にしてください。

[迷信] scanf ではバッファオーバーランを防げない:
http://www.kijineko.co.jp/tech/superstitions/buf …

基本的に、入力を伴う時、文字列でやり取りした方が「安全」だと言う事です。
%11s%*[^\n]%*c
の部分は性器表現、もとい、正規表現、と言われるやり方で、11文字になった時点でそれ以上の入力を受け付けないようにするオマジナイです。そうすれば、指定した文字数以上のメモリへの書き込みを抑制する事が出来ます。
ちなみに、11文字、と言うのは、電話番号の入力への使い回しも考えて、巷の電話番号の「最大の長さ」を考慮した文字数です。
    • good
    • 0
この回答へのお礼

scanfの部分理解できました!
ありがとうございます。
教えて頂いた部分を修正したのですが、うまくいきませんでした…
longの部分は指定されていて、変更はしなかったのですが、エラーがx[y].namaeをx[y]->namaeに変えろというもので、すべて変更したところまたそこの部分がえらーになってしまい…
何度も申し訳ないのですがご助言頂けないでしょうか。

typedef struct Data{
char *namae;
char *sei;
long bangou;
}DATE;
int main(void){
DATE *x[100];

char s[12];
char t[12];
long r[12];

switch(a){
case 1:
printf("何番目のデータを入力したいですか:");
scanf("%d",&y);
printf("名前を入力:");
scanf("%11s%*[^\n]%*c",s);
x[y].namae=(char*)malloc(sizeof(char)*(strlen(s)+1));
strcpy(x[y].namae,s);
printf("性別を入力:");
scanf("%11s%*[^\n]%*c",t);
x[y].sei=(char*)malloc(sizeof(char)*(strlen(t)+1));
strcpy(x[y].sei,t);
printf("電話番号を入力:");
scanf("%11ld%*[^\n]%*c",r);
x[y].bangou=(char*)malloc(sizeof(char)*(strlen(r)+1));
strcpy(x[y].bangou,r);
break;
case 2:
for(z=1;z<=y;z++){
printf("%s",x[z].namae);
}

修正部分だけ掲載しました。

お礼日時:2020/12/24 21:54

scanf("%s",&x[y].namae);


が何をするか理解できている?
scanf("%ld",&x[y].bangou);
x[y].bangou=(char*)malloc(sizeof(char)*(strlen(&x[y].bangou)+1));
がおかしいことに気づいてる?

勝手に補足しておくと
%11s: (最初の方にある空白文字を無視して) 最大 11個の「空白でない文字」を読み込み, 対応する (char * を期待する) 引数に入れる (そして最後に '\0' を追加する)
%*[^\n]: 改行以外の文字 ([^\n]) を読み込んで (* が指定されているので) 捨てる
%*c: 1文字読み込んで捨てる
ということ.

%s では空白が読み込めないので, それが問題になるときは別の方法を考えてください.

ところで, DATE でいいの? 構造体タグが Data だから, 全部大文字にするなら DATA だと思うんだ. あと構造体タグと typedef名に同じものが使えるというのも, 知っておくと便利かもしれない.
    • good
    • 0
この回答へのお礼

理解したいとは思っていますが、充分出来ていないと思います…
おかしくないと思っていたのですが、違いました。
scanf部分の説明とてもわかり易かったです。理解できました。
構造体タグと同じものが使えるのは知りませんでした。ご教授ありがとうございます。

お礼日時:2020/12/24 21:57

> 何度も申し訳ないのですがご助言頂けないでしょうか。



あー、多分ここがダメなんじゃないかな。

> DATE *x[100];

「構造体の配列」でしょ?これじゃあポインタなんだか配列なんだか分かんない。そうじゃなくって、

DATE x[100]

で充分じゃないですか?文字通り「構造体の配列」が要り用なんで、ポインタのなんたら、が欲しいわけじゃない。
ここだけ修正すれば、まずはエラーが取れるでしょう。
それと、

x[y].bangou

はlongで宣言したまま、って事ですよね。そうすると、scanfでこの部分に入力した「文字列」はそのままだと格納出来ないんで、atolでlongに変換します。
従って、この部分「だけ」は

x[y].bangou = atol(r);

でlongに変換した文字列rを直接代入するだけ、で済みますね。
    • good
    • 0
この回答へのお礼

ありがとうございます。
以下のようなエラーはどう修正すれば良いのでしょうか…
訳したのですがどうすればよく分からなくて。
本当に何度もご助言ありがとうございます。
Main.c:44:63: warning: incompatible pointer types passing 'long [12]' to parameter of type 'const char *' [-Wincompatible-pointer-types]
x[y].bangou=(long)malloc(sizeof(long)*(strlen(r)));
^
/usr/include/string.h:385:35: note: passing argument to parameter '__s' here
extern size_t strlen (const char *__s)
^
Main.c:45:34: warning: incompatible pointer types passing 'long [12]' to parameter of type 'const char *' [-Wincompatible-pointer-types]
x[y].bangou=atol(r);
^
/usr/include/stdlib.h:366:26: note: passing argument to parameter '__nptr' here
__NTH (atol (const char *__nptr))
^
2 warnings generated.

お礼日時:2020/12/24 23:20

> 以下のようなエラーはどう修正すれば良いのでしょうか…



いやいや、だから・・・。
元々、構造体DATEは次のような定義でしょ?

typedef struct Data{
 char *namae;
 char *sei;
 long bangou;
}DATE;

名前も性もポインタだけど、番号は違うじゃない。「フツーにlongの整数」として宣言してますよね?
こいつがポインタ変数じゃなく、単なる変数である以上、メモリアロケーションは必要ないんですよ。

例えば構造体じゃなくても、

int x = (int *)malloc(何とやら)

ってメモリアロケーションしたら似たようなエラーが出ると思いますよ。こいつ(x)はポインタ型じゃない。

だからメモリアロケーションを行わずに、

x[y].bangou = atol(r);

でそのまま代入出来ます。

int x = 1;

なんかと同じです。
    • good
    • 0
この回答へのお礼

詳しく説明ありがとうございます。
理解できました!
修正したあと、以下のようなエラーがでたのですがこれはlong r[12]を何かに変更しなければイコールにはならないということですか?訳したところポインタが関係しているのかと思ったのですがナンバーはポインタではないのでどのような事を言っているのかが分かりません…

Main.c:44:34: warning: incompatible pointer types passing 'long [12]' to parameter of type 'const char *' [-Wincompatible-pointer-types]
x[y].bangou=atol(r);
^
/usr/include/stdlib.h:366:26: note: passing argument to parameter '__nptr' here
__NTH (atol (const char *__nptr))
^
1 warning generated.

お礼日時:2020/12/25 00:13

そこで出てる警告は


strlen(r)

atol(r)
の実引数 r が関数の期待する型じゃないよってこと. リファレンスなんかで確認しよう.

ちなみに C の場合ポインタと整数は互いに変換できるので
int x = (int *)malloc(何とやら)
はエラーにならない>#5. 「エラーにならない」だけで, それが本当に期待する動作なのかどうかは知らない.
    • good
    • 0
この回答へのお礼

ありがとうございます。rの型のせいでイコールが成立たないということですかね。
電話番号はポインタを使っていないはずなのですが、どこかでポインタになっているみたいなエラーでよく分かりません。
リファレンスという言葉を浅学で申し訳ないのですが知りませんでした。
直訳が「参考資料」だったのですがネット等の参考資料に例があるということでしょうか。
互いに交換できるのも知りませんでした。おかしな操作が内部で行われていてもエラーにはならないということですね。
今後気をつけたいです。

お礼日時:2020/12/25 00:21

> 修正したあと、以下のようなエラーがでたのですがこれはlong r[12]を何かに変更しなければイコールにはならないということですか?



あああああ、そこは

> long r[12]

だとlongの配列になってるじゃない。
だからそこを

> char r[12]

にしておけば大丈夫でしょ。すっかり見落としてました。
long r[12]のままだと、文字列読み込んでるのに、対象がlongの配列なんで文句言われてるのです。

あとね、ぶっちゃけ、そこで配列3つも用意しておくのは無駄なんで、1つ

char s[12]

だけ用意しておいて、「使い回し」して大丈夫です。scanfで読み込む度に中身は書き換わりますから。
    • good
    • 0
この回答へのお礼

できました!ありがとうございます。

お礼日時:2020/12/25 00:31

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