• ベストアンサー

構造体の配列について(2)

前回「構造体の配列について」という質問タイトルで、質問させていただいたのですが、理解が完全ではないため同じようなプログラム内容ではありますが、疑問を書かせていただきます。よろしくお願いします。 ----------------------------------------------------------------------- #include<stdio.h> #include<string.h> struct person{ char name[1];    //// (1) //// int height; int weight; }; int main() { struct person dt[10]; strcpy(dt[0].name,"日本一郎"); strcpy(dt[2].name,"関東次郎"); strcpy(dt[9].name,"関西三郎"); dt[1].weight=99; dt[1].height=168; printf("%s %s %s %d %d \n",dt[0].name,dt[2].name,dt[9].name,dt[1].weight,dt[1].height); return 0; } ----------------------------------------------------------------------- 以上のプログラムの(1)の部分で、文字を1文字しか格納出来ないのに、 strcpy(dt[0].name,"日本一郎"); strcpy(dt[2].name,"関東次郎"); strcpy(dt[9].name,"関西三郎"); としても何故正しく実行できるのかわかりません。 前回いろいろとご回答いただいたのに、しっかりと理解できない者ですが、教えていただければ嬉しいです。

質問者が選んだベストアンサー

  • ベストアンサー
  • S117
  • ベストアンサー率40% (18/45)
回答No.13

>#11のお礼 ◎2 ちょっと違います。この場合height weightともにまだデータが破壊されていません。 私の回答が少しわかりづらかったですね。補足しておきます。 私は2つの現象について言及しています。 例1 >不正に書き換えた後(メモリ破壊)に、そこになにかを書き込むまえに読み込むと、値が不正になっているのです。 ---------------------------------------------- dt[0].height = 168; //(1) dt[0].weight = 99; //(2) strcpy( dt[0].name, "日本一郎" ); //(3) printf( "氏名: %s\n", dt[0].name ); printf( "身長: %d\n", dt[0].height ); printf( "体重: %d\n", dt[0].weight ); ---------------------------------------------- この場合、(1),(2)で設定された値が(3)で不正に書き換えられています。そして、不正な値が表示されるでしょう。 例2 >そこ(破壊された場所)に値を書き込む操作自体には影響が出ません。 ---------------------------------------------- strcpy( dt[0].name, "日本一郎" ); //(1) dt[0].height = 168; //(2) dt[0].weight = 99; //(3) //printf( "氏名: %s\n", dt[0].name ); //(4) printf( "身長: %d\n", dt[0].height ); printf( "体重: %d\n", dt[0].weight ); ---------------------------------------------- この場合も、(1)が不正なデータの書き換えをしています。 ところが、その後(2)(3)でデータを書き換えます。 結果、表示時に身長と体重は正常なデータです。 なお、(4)で名前を表示しようとすると、データの内容が1文字目しか維持できる保証がないために、何が起きるかわかりません。なので、コメントアウトしてあります。 なお、#9のお礼では、あなたは例2の場合に(2)(3)の操作自体が不正だと考えているように読めましたので、#11での指摘となりました。

muffler
質問者

お礼

>strcpy( dt[0].name, "日本一郎" ); //(1) >dt[0].height = 168; //(2) >dt[0].weight = 99; //(3) >//printf( "氏名: %s\n", dt[0].name ); >printf( "身長: %d\n", dt[0].height ); >printf( "体重: %d\n", dt[0].weight ); (1)により「height」、「weight」のメモリに不正なデータ書き換えを行っても、(2)、(3)を後に書くことにより、「height」、「weight」のメモリに正常なデータを書き換えられるということですね!

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (13)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

この話は「構造体の配列」とは全く関係ありません. #1, #2 でいわれているように「たまたま正しく実行できているように見えるだけ」です. printf の前に strcpy(dt[1].name,"北風小僧"); dt[0].weight = 0x22334455; の 2行を入れてやると楽しいことになるかもしれない.

muffler
質問者

お礼

「日本一郎UD3"北風小僧 関東次郎 関西三郎 0 1838263439」 となってしまいました。。

全文を見る
すると、全ての回答が全文表示されます。
noname#144013
noname#144013
回答No.2

こんにちは。 ご提示のソースを以下のように変更して実行してみて下さい。 ※"@追加"の部分を付け加えて下さい。 注)インデントのため、全角スペースを入れています。   コンパイルの際は、削除するか半角スペースorタブに置き換えて下さい。 ------------------------------------------------------------ #include<stdio.h> #include<string.h> struct person{   char name[1]; //// (1) ////   int height;   int weight; }; int main() {   struct person dt[10];   // @追加(1-1)   dt[0].height=111;   dt[0].weight=222;   // @追加(1-2)   printf("dt[0].height=%d, dt[0].weight=%d\n", dt[0].height, dt[0].weight);   strcpy(dt[0].name,"日本一郎");   // @追加(2-1)   printf("dt[0].height=%d, dt[0].weight=%d\n", dt[0].height, dt[0].weight);   strcpy(dt[2].name,"関東次郎");   strcpy(dt[9].name,"関西三郎");   dt[1].weight=99;   dt[1].height=168;   printf("%s %s %s %d %d \n",dt[0].name,dt[2].name,dt[9].name,dt[1].weight,dt[1].height);   return 0; } ------------------------------------------------------------ 上記を実行すると、以下の2行   dt[0].height=111;   dt[0].weight=222; でセットしたデータが変わっている(壊されている)のが解ると思います。 この理由をご自身で考えてみて下さい。 #1さんが仰られているとおり、 質問者さんが、ご提示されたプログラムは、見かけ上たまたま問題なく実行されるかも しれませんが、実は、内部データ(今回は構造体のデータ)が壊されています。 ※場合によっては、実行時エラーになる可能性があります。 <補足> strcpy関数の2つの引数は、どちらも文字列へのポインタを渡すようになっています。 従って以下の記述   strcpy(dt[0].name,"日本一郎"); は、2つの引数とも文字列へのポインタになっていますので、この記述自体には何の問題 もありません。(引数の型が合っているため、コンパイルエラーにはならない) 従って、実行時も問題なく文字列がコピーされます。あるいはコピーしようとします。 (結果としてエラーが発生するかもしれませんが。。。) ※プログラム(コンピュータ側)としては、作成者が指示したとおりの処理を行います。 コンパイラとしては、strcpy関数の引数 "dt[0].name"は、単純にポインタとしてみている ので、その実際の中身がどうなっているか(配列サイズ、構造体のメンバ構成など)までは チェックできません。(と言うかチェックする必要はない) ※この辺りのことは、プログラムの作成者が、しっかりとデータ構造などを理解&把握し  てプログラミングすべき部分であり、気をつけないといけない部分だと思います。

muffler
質問者

お礼

ご回答ありがとうございます。 確保したメモリ領域を越えて、メモリを使ったため不具合が起こっているという考えなのでしょうか? 初心者なため、詳しくご回答いただいたのに完全に理解できなくてすいません。

全文を見る
すると、全ての回答が全文表示されます。
  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.1

>何故正しく実行できるのかわかりません。 正しく実行している「ように見える」、というのが正しいです。 実際には、配列の定義範囲外の領域を「破壊しています」。 それが顕在化するのは、「次回」その破壊した領域に アクセスしようとしたときです。 顕在化したとき、どうなるかはわかりません。 何も起きないかもしれませんし、プログラムが突然異常終了するかもしれません。

muffler
質問者

お礼

ご回答ありがとうございます。 >配列の定義範囲外の領域を「破壊しています」。 上記の事が起きるとわかりました。 具体的にどうなっているか、勉強してみます。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 構造体の配列について

    --------------------------------------------------- #include<stdio.h> #include<string.h> struct person{ char name[80]; int height; int weight; }; int main() { struct person dt[10]; strcpy(dt[1].name,"日本太郎");    //// (1) //// dt[1].weight=70; dt[1].height=180; dt[5]=dt[1];    //// (2) //// printf("%s %d %d \n",dt[1].name,dt[1].weight,dt[1].height); return 0; } ----------------------------------------------------- 以上のプログラムは参考書に記述されていたものですが、(1)の部分で、配列名dtに「"日本太郎"」を設定するならわかるのですが、配列の1つの要素「dt[1]」に「"日本太郎"」を設定しているというように見え、これはエラーが出ると感じたのですが出ません。 char dt[10]; strcpy(dt[1],"日本太郎"); 以上のようにしてしまっているというイメージがあります。 後、(2)の部分もよくイメージがわきません。 以上、どのような仕組みになっているのか教えていただければ嬉しいです。

  • C言語/構造体の宣言と初期設定について

    C言語初心者です。 私が使用している参考書の記載に誤りがあるのか確認したくて質問させて頂きました。 宜しくお願い致します。 struct person{  char name[40];  int height;  int weight; } struct person dt = {"山田太郎", 70, 180}; printf("%s %d %d\n", dt.name, dt.weight, dt.height); このコードで出力されるのは、 山田太郎 70 180 とのことですが、 宣言時のメンバの順番と、 初期化時の値の順番が、 上の記述だと一致していないように私には思えます。 参考書にある上記の記載は、はたして、正しいのでしょうか?

  • 構造体についてです。

    身体測定表を作っていて最初に作ったデータを変更して表示したいのですがNo1の168cm→162cm No2の74kg→74.5kg  No3の20歳→19歳 No4の田中三郎→小林三郎 (No3と4は構造体ポインタを使ってデータを変更) 全データを表示させてその後No1と3の名前~体重を丸ごと入れ替えて全データを表示させたいのですがエラーがでてしまいます。 分かる人がいましたらどこを直したらいいか教えて下さい。 #include <stdio.h> #include <string.h> struct data{ int no; char name[21]; int age; int height; float weight; }; struct data *sp; void main(){ int i; struct data a[4]={ {1,"鈴木太郎",23,168,60}, {2,"山本次郎",17,180,74}, {3,"山田花子",20,156,53}, {4,"田中三郎",35,172,68}, }; struct data b; for(i=0;i<4;i++){ printf("番号 :%d\n",a[i].no); printf("名前 :%s\n",a[i].name); printf("年齢 :%d歳\n",a[i].age); printf("身長 :%dcm\n",a[i].height); printf("体重 :%4.1fkg\n\n",a[i]. weight); } a[0].height=162; a[1].weight=74.5; *sp[2].age=19; strcpy(*sp[3].name,"小林三郎"); for(i=0;i<=4;i++){ b=a[0]; a[0]=a[2]; a[2]=b; a[0].no=1; a[2].no=3; } for(i=0;i<=4;i++){ printf("番号 :%d\n",a[i].no); printf("名前 :%s\n",a[i].name); printf("年齢 :%d歳\n",a[i].age); printf("身長 :%dcm\n",a[i].height); printf("体重 :%4.1fkg\n\n",a[i].weight); } }

  • 構造体配列

    いつもお世話になっています。 今日構造体配列を勉強した際に下の演習をしていたのですが、いまいち基礎的な関数や配列のことやコマンドライン引数のことが理解できていません。一応自分なりにプログラムを書いてみたのですが、エラーがでます。 どなたかアドバイス・ご指摘・解説・例などをおねがいいたします。 問題はコマンドライン引数で人数を指定し,人数分のデータを標準入力(キーボード)から構造体配列に入力し,標準出力(ディスプレイ)に出力するプログラムです。 #include<stdio.h> typedef struct{ char name[100]; int age; double height; double weight; } PERSONAL_DATA1; typedef struct{ int person[10]; }PERSONAL_DATA2; PERSONAL_DATA1 input_personal_data(int num); void output_personal_data(PERSONAL_DATA2 person[10],int num); int main(int argc, char *argv[]){ PERSONAL_DATA1 x; int num,i; char person[10]; if(argc != 2){ printf("Usage ./test number"); return 0; } num=atoi(argv[1]); for(i=0;i<10&&i<num;i++){ person[i]=input_personal_data(); output_personal_data(person,num); return 0; } } PERSONAL_DATA input_personal_data(void){ PERSONAL_DATA1 x; printf("name>"); scanf("%c" ,&x.name); printf("age>"); scanf("%d" ,&x.age); printf("height>"); scanf("%lf" ,&x.height); printf("weight>"); scanf("%lf" ,&x.weight); return x; } void output_personal_data(PERSONAL_DATA2 person[10],int num){ PERSONAL_DATA1 x; printf("name>>%s\n" ,x.name); printf("age>>%d\n" ,x.age); printf("height>>%lf[cm]\n" ,x.height); printf("weight>>%lf[kg]\n" ,x.weight); } またコマンドライン引数で指定したデータファイルAから入力した個人データ集合を構造体配列に取り込み,標準出力(ディスプレイ)と同じくコマンドライン引数で指定したデータファイルBへファイル出力に出力するプログラムというおまけの問題もあります。 こちらの問題のヒントもいただけませんか?

  • C言語のプログラムで...

    下のプログラムは参考書にあったサンプルプログラムなのですが /* 関数の宣言 */ int print_struct(struct person dat); のところはなぜint型なのですか? #include <stdio.h> #include <string.h> /* 構造体の定義 */ struct person { char name[20]; /* 名前 */ double height; /* 身長 */ double weight; /* 体重 */ int bpl; /* 最低血圧 */ int bph; /* 最高血圧 */ }; /* 関数の宣言 */ int print_struct(struct person dat); /* main関数 */ int main(void) { /* 変数の宣言 */ struct person dat; /* 構造体のメンバーに値を代入 */ strcpy(dat.name,"山田太郎"); dat.height = 173.5; dat.weight = 63.0; dat.bpl = 98; dat.bph = 113; /* struct person関数を実行 */ print_struct(dat); return 0; } /* print_struct関数 */ int print_struct(struct person dat) { /* 画面に出力 */ printf("%s\n",dat.name); printf("%f\n",dat.height); printf("%f\n",dat.weight); printf("%d\n",dat.bpl); printf("%d\n",dat.bph); return 0; }

  • プログラミング構造体について。

    include<stdio.h> #include<stdlib.h> #include<string.h> struct person{ char name[10]; int gender; int age; }; void printPersonList(struct person *person_p, int size); void outputPersonList(struct person *person_p, int size); double getAverageOfAge(struct person *person_p, int size); int countMales(struct person *person_p, int size); int countFemales(struct person *person_p, int size); int main(void){ struct person *person_p; int i, count, gender, age, maleCount, femaleCount; char name[20]; double average; printf("登録する人数を入力してください。\n"); scanf("%d", &count); person_p = (struct person*)malloc(sizeof(struct person)* count); for(i=0; i < count; i++){ printf("名前・性別(男性:0, 女性:1)・年齢をスペース区切りで入力してください。\n"); scanf("%s %d %d", name, &gender, &age); strcpy((person_p + i) -> name, name); (person_p + i) -> gender = gender; (person_p + i) -> age = age; } printPersonList(person_p, count); outputPersonList(person_p, count); average = getAverageOfAge(person_p, count); printf("平均年齢:%f\n", average); maleCount = countMales(person_p, count); femaleCount = countFemales(person_p, count); printf("男性:%d名, 女性:%d名\n", maleCount, femaleCount); free(person_p); return 1; } void printPersonList(struct person *person_p, int size){ int i; printf("登録リスト\n"); printf(" name | gender | age\n"); printf("----------+--------+-----\n"); for(i=0; i < size; i++){ printf("%10s | %1d | %2d\n", (person_p + i) -> name, (person_p + i) -> gender, (person_p + i) -> age); } } void outputPersonList(struct person *person_p, int size){ FILE *output; int i; if((output = fopen("meibo.c", "w")) == NULL){ printf("meibo.cを開けませんでした。\n"); return; } for(i=0; i < size; i++){ fprintf(output, "%s, %d, %d\n", (person_p + i) -> name, (person_p + i) -> gender, (person_p + i) -> age); } fclose(output); } 残り3つの関数をすべて定義する(それぞれ10行程度) getAverageOfAge, countMales, countFemales どう定義すればいいのか教えてください。お願いします。

  • C言語の構造体の配列の扱い方

    #include <stdio.h> struct record { char name[10]; float height; float weight; }; /* 各データを、長さ 5 の record 構造体の配列に代入 */ struct record records[5] = { { "yasuo", 170.5, 70.5 }, { "hideaki", 176.5, 65.8 }, { "nobu", 166.5, 58.2 }, { "yuichi", 168.0, 65.4 }, { "nori", 152.7, 68.6 } }; float std_weight(struct record r){ /* 標準体重 = (身長 - 100) * 0.9 */ return (r.height - 100.0) * 0.9; } float std_weight(struct record r); となっている時に、メイン関数にて、5人の身長をもとめる方法と適正標準体重を求める方法を教えてください。構造体の配列を一括で扱うコードがいまいちわかりません。おねがいします。

  • リスト構造がうまく動きません!!

    C言語で以下のようなプログラムを作りました。 「main関数内で下記のデ-タを構造体に格納し、キーボードから入力された名前と該当する学生の身長と年齢を画面に表示するプログラムを作成せよ。」というものです。 このプログラムはコンパイルは通るのですが、2人目以降のデータを表示させようとしても表示してくれません。。。どうもリスト構造のfor文がうまくループしていないみたいなんですが原因が分かりません。どなたか原因の分かる方アドバイスをお願いしますm(_ _)m #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct data{ char name[20]; int height; int age; struct data *next; }person; person *newperson(void); int main(void){ char namae[20],s[20]; int toshi,shinchou,i; person *head; person *list; person *nlist; person *LIST; head = newperson(); nlist = head; printf("データを入れてください。\n"); for(i=0;i<=4;i++){ scanf("%s",namae); scanf("%d",&shinchou); scanf("%d",&toshi); list = newperson(); strcpy(list ->name,namae); list -> height = shinchou; list -> age = toshi; nlist -> next = list; nlist = list; } printf("知りたい人の名前は?\n"); scanf("%s",s); for(LIST = head->next;LIST ->next != NULL;LIST = LIST->next){ if(strcoll(s,LIST ->name)==0){ printf("%s\t%d\t%d\n",LIST->name,LIST->height,LIST->age); break; } printf("%s\n",LIST->name); printf("%s\n",LIST->next->name); } return(0); } person *newperson(){ person *dummy; dummy = (person*)malloc(sizeof(person)); dummy -> next = NULL; return(dummy); }

  • 構造体について

    5件のデータをRECORDに追加したいのですが、 RECORD inputdata(void)の宣言文エラーなどのコンパイルエラー。それと、inputdataを用いてどうやって5件のデータを入れたらいいかがわかりません。教えてください。 #define SIZE 5 #include <stdio.h> typedef struct{ int yy; int mm; int dd; }YMD; typedef struct{ char name[20]; YMD birthday; int age; }RECORD; RECORD inputdata(void); void main(void) { int i; for(i = 0;i < SIZE;i++){ inputdata(); } RECORD inputdata(void) { RECORD person; printf("名前>"); scanf("%s", person.name); printf("誕生日入力\n"); printf("年>"); scanf("%d", person.birthday.yy); printf("月>"); scanf("%d", person.birthday.mm); printf("日>"); scanf("%d", person.birthday.dd); return person; }

  • 構造体のポインタ

    なぜかprevのほうが表示されません。 問題としては関数を作成し gyuri[23] -> sunyon[23] -> nicole[20] -> hara[20] -> jiyon[17] -> hara[20] -> nicole[20] -> sunyon[23] -> gyuri[23] -> END と表示させるのが目的です gyuri[23] -> sunyon[23] -> nicole[20] -> hara[20] -> jiyon[17] ここまではうまく表示できているのですが・・・ #include <stdio.h> void printoufuku(struct kara *p); struct kara { char name[16]; int age; struct kara *next; struct kara *prev; }; int main() { struct kara a, x, f, m, c, *start; strcpy(a.name, "gyuri"); a.age = 23; strcpy(x.name, "sunyon"); x.age = 23; strcpy(f.name, "nicole"); f.age = 20; strcpy(m.name, "hara"); m.age = 20; strcpy(c.name, "jiyon"); c.age = 17; a.next = &x; x.next = &f; f.next = &m; m.next = &c; c.next = NULL; /********************* 5 lines */ c.prev = &m; m.prev = &f; f.prev = &x; x.prev = &a; a.prev = NULL; /*********************/ start = &a; printoufuku(start); return 0; } void printoufuku(struct kara *p) { for(p->next; p != NULL;p = p->next){ printf("%s[%d] ->",p->name,p->age); } for(p->prev; p != NULL; p = p->prev){ printf("%s[%d] ->",p->name,p->age); } }