• ベストアンサー

2次元配列定義後の、個々の要素への代入

以下のようなプログラムを作ったのですが、 /* n個の名前を収録している電話帳の中から、ある人の名前を見つけるプログラム */ /* ・電話帳に記載されている情報は名前と電話番号のみとする */ /* ・氏名検索による一致にて発見とみなす */ /* ・電話帳のデータは予め入力されているとする */ #include <stdio.h> #include <string.h> #define N 100 int main(void) { int i, j; /* カウンタ変数 */ char name[N][20]; /* 名前のデータの入った配列.name[番号(人数)][最大文字数] */ char samp[2][20] = {"David","Sunrise"}; /* 発見したい人物の氏名 */ name[99] = "David"; /* お試しでデータの最後の方に一致する文字列を入れてみる */ printf("name[99]=%s",name[99]); /* 1行上で代入が行われているかの確認 */ for(i=0;i<=N;i++){ if(strcmp(name[i],samp[0])==0){ printf("The name ""David"" is included in this list.\n"); printf("Resistered number is No.%d.\n",i); } } printf("This search program ended.\n"); } name[99] = "David"; の文でちゃんと代入されていないようなのです。でコンパイルすると「エラー E2277 calculation-Cost2.cpp 18: 左辺値が必要(関数 main() )」と出てきて、一応実行できるんですが、一番最後のprintf文が表示されるだけでfor文が機能していません。。 すみませんがどなたか教えていただけないでしょうか?よろしくおねがいしますm(_ _)m

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

  • ベストアンサー
  • dra2jp
  • ベストアンサー率25% (18/72)
回答No.4

まずプログラムを見ながら回答をご覧下さい。 char name[5][10]={"David","Mathew","Linda","Pole","Thomas"}; char comp[5]="Pole"; このように宣言しているにもかかわらず、 if(name[i][10]==comp[5]) こう比較するのはおかしいですね? 10個配列要素を持てと宣言したのですから 配列要素は[9]までしかないはず。 つまりname[i][10]はなくname[i][9]までしかないんです。 ではナイといえども何か入っているはず。 name[i][10]には一体何が入っているのか? 前に私が言ったポインタの話を思い出してください。 配列は連続したメモリのアドレスにそれぞれ格納されています。 char name[3][3]; と宣言したならば name[0][0]には123420番地 name[0][1]には123421番地 name[0][2]には123422番地 配列は連続したメモリのアドレスに確保されます。 では次は? name[0][0]には123420番地 name[0][1]には123421番地 name[0][2]には123422番地 name[1][0]には123423番地 name[1][1]には123424番地 name[1][2]には123425番地 name[2][0]には123426番地 name[2][1]には123427番地 name[2][2]には123428番地 そうです。連続で宣言した場合、全て連続したアドレスに用意されます。 メモリの番地が連続している事をご確認ください。 何が言いたいかと言うと 今、配列要素は3つしか指定していないので name[0][3]という配列はないはずですね? しかしここが示すポインタ(番地)はどこを意味するでしょうか? name[0][2]の一つ番地の多い所。 つまりこの場合 name[1][0]を指している事になります。 おもしろいですね。よく考えないと解らない問題だと思います。 なんのこっちゃ? そう思うのも当然でしょうけど、とりあえず聞いてください。 宣言したよりも一つ多い配列要素を示す つまり name[i][10] を示すと name[i+1][0] を示した事になります。 例を出すと name[0]に"David"が name[1]に"Mathew"が 入っているのならば name[0][10]を示すと name[1][0]である"M"が示される事になります。 この事を踏まえ、name[0]には何が入っているか確認してみましょう。 name[0][0] == "D" name[0][1] == "a" name[0][2] == "v" name[0][3] == "i" name[0][4] == "d" name[0][5] == "\0" name[0][6] == (null) 何もないという意味です。 name[0][7] == (null) name[0][8] == (null) name[0][9] == (null) name[0][10]== "M" 一方comp[5]は comp[0] == "P" comp[1] == "o" comp[2] == "l" comp[3] == "e" comp[4] == "\0" comp[5] == (null) nameは連続して配列を宣言しましたがcompはもう次に配列を宣言していないので、 comp[5]は何も入っていません。 つまりnullです。 わかりましたか? つまりi==0の時、 if(name[i][10]==comp[5]) は"M"とnullを比較している事になるのです。 if("M" == null) これは違いますね。 同様にname[1][10]には次の配列の先頭の文字である"L"がはいります。 つまりi==1の時、 if(name[i][10]==comp[5]) は"L"とnullを比較している事になるのです。 if("L" == null) これも違いますね。 途中省略して name[4][10]には何が入っているか? 連続して配列を宣言した場合、連続なアドレスが用意されますが、name[4]は連続して宣言した配列の 一番最後。 つまりname[4]の次はもうない(null)わけで、nullです。 だからname[4][10]はnullです。 つまりi==4の時、 if(name[i][10]==comp[5]) はnullとnullを比較している事になるのです。 if(null == null) これは合いますね。つまりiが4の時のみ一致するという事です。 これらを確認するためにサンプルプログラムを書きました。 プログラムは実際に実行して見る事がなにより理解できると思いますから。 #include <stdio.h> int main(void) { int i,j; char name[5][10]={"David","Mathew","Linda","Pole","Thomas"}; char comp[5]="Pole"; for(i=0;i<=4;i++){ printf("********** i==%dの時 **********\n",i); for(j=0;j<=10;j++) printf("name[%d][%d]=%c\n",i,j,name[i][j]); printf("\n今、name[%d][10]は\"%c\"、comp[5]は\"%c\"。つまりこの時",i,name[i][10],comp[5]); if(name[i][10]==comp[5]){ printf("一致するという事です!\n\n\n"); } else printf("一致しないという事です。\n\n\n"); } return 0; } ポインタについて慣れていないと頭を抱える話になってしまったと思いますけど、 よくよく読んでみてください。 何か疑問があればまた聞いてください。

chee-choff
質問者

お礼

す、すごいです…ふむふむ!なるほろ!! としか言いようがありません汗 >配列は連続したメモリのアドレスに確保されます。 これ知りませんでした!!2次元配列の等しい前1次元に対する後1次元のアドレスは連続するのだろうと思っていましたが、考えてみれば連続して定義したんだからアドレスも連続しなきゃおかしい…というかこんがらがりますよね。 それで1つ疑問なのですが、 >一方comp[5]は > >comp[0] == "P" >comp[1] == "o" >comp[2] == "l" >comp[3] == "e" >comp[4] == "\0" >comp[5] == (null) > >nameは連続して配列を宣言しましたがcompはもう次に配列を宣言して>いないので、 >comp[5]は何も入っていません。 >つまりnullです。 あともう1つ上で回答してくださったWernerさんの >>>#5 >> name[0][6] == (null) 何もないという意味です。 >分かりやすく説明するためにあえてnullが入っていると言うことにしたのかもしれませんが、 >実際には配列の初期化時に値が明示的に指定されなかった要素は0で初期化されます。 >(今回の場合char型なので'\0'が入っているともいえます。) この辺の話なのですが、確かCかFortran77の講義で「変数を割り当てていないアドレスには予め何らかの値が入っている」と習って、実際定義前の適当なアドレスの中身を表示させるとアトランダムな値が出てきた、という記憶があるのですが、記憶違いなんでしょうか?今回のcomp[5]のように char comp[5]="Pole"; の割り当てからnull文字が入ったcomp[4]の次のアドレスを表すcomp[5]なので初期化される、という理由ならこの私の疑問は見当違いな話なんですが。ちなみに講義ではLinuxを使ってました…関係ないとは思いますが一応。。

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

その他の回答 (9)

  • Werner
  • ベストアンサー率53% (395/735)
回答No.10

> ここで言うcomp[5](定義する要素数5の配列でなく、定義後(この要素は定義されていませんが)の配列compの6番目の要素)というのは、 > nullでなく何かしら不定の値が入っているということなんですよね? そうです。 #9でも触れましたが、値が不定と言うだけでなく 他の変数が使っている領域かもしれないという危険もありますから、 参照も代入もしないようにしましょう。 なお、C言語でNULLというとヌルポインターの事を指しますが、 char型はポインター型ではないのでNULLは持てません。 '\0'のことをヌル文字と言うことはありますが、 名前が似ているだけでヌルポインターとは別のものです。 (ヌル文字の意味でNULLを使うと混乱を招くかもしれません。) http://www.kouno.jp/home/c_faq/c5.html#13

すると、全ての回答が全文表示されます。
  • Werner
  • ベストアンサー率53% (395/735)
回答No.9

> char comp[5]="Pole"; > の割り当てからnull文字が入ったcomp[4]の次のアドレスを表すcomp[5]なので初期化される、 > という理由ならこの私の疑問は見当違いな話なんですが。 この場合comp[5]には変数が割り当てられていないので 何が入っているかは分かりません。 他の変数などに割り当てられている可能性もあるため comp[5]は使ってはいけません。 なお、初期化をしていない場合も値は不定となります。 もし char comp[6]="Pole"; としていれば、配列初期化の対象になるので comp[5]は0('\0')になります。 また、 char comp[6]; strcpy(comp,"Pole"); とした場合は、初期化も代入もされていないcomp[5]は不定です。 >>#8 > ゴミゴミとは言いますが、実際格納されているこの値は何なんでしょうね? 以前そのアドレスに格納されていた値がそのまま残っている場合がほとんどでしょうね。 デバッグモードなどだと何か特定の値を入れてくれることもあるかもしれません。 試しに、VS 2005のデバッグビルドで試してみたら初期化していない値を参照するプログラムは 実行時にちゃんとエラーを出してくれました。 リリースビルドやcl.exeのオプションなしコンパイルでは エラーを出さなくなって2015115244みたいな値が出てきますね。

chee-choff
質問者

お礼

むーぅ… 同じことをつらつらと言っていたらホントに申し訳ないんですが、 それでは、 >一方comp[5]は > >comp[0] == "P" >comp[1] == "o" >comp[2] == "l" >comp[3] == "e" >comp[4] == "\0" >comp[5] == (null) > >nameは連続して配列を宣言しましたがcompはもう次に配列を宣言して>いないので、 >comp[5]は何も入っていません。 >つまりnullです。 ここで言うcomp[5](定義する要素数5の配列でなく、定義後(この要素は定義されていませんが)の配列compの6番目の要素)というのは、nullでなく何かしら不定の値が入っているということなんですよね?

すると、全ての回答が全文表示されます。
  • dra2jp
  • ベストアンサー率25% (18/72)
回答No.8

ちなみに今回の場合、Werner様のおっしゃるとおり、 char name[10]="David"; と宣言した場合、 [5]~[9]は全てヌル文字\0が入っていました。 ご参考までに。 確かに何も初期化していない変数にはゴミが詰まっています。 どうでもよい値が格納されているようですが、 #include <stdio.h> int main(void) { int i,j; printf("初期化していない値は%d %d.\n",i,j); return 0; } こちらのサンプルで確認できます。 ゴミゴミとは言いますが、実際格納されているこの値は何なんでしょうね? 実行結果は何度やっても 初期化していない値は1 11544720. でした。う~ん、1ってなんだろう。 また、studio.NETでは自動的に割り当てられていない変数には0が格納されるみたいで、0しか表示されませんでした。

すると、全ての回答が全文表示されます。
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.7

他の方が指摘していないトコだけ >for(i=0;i<=N;i++){ は、 for(i=0;i<N;i++){

chee-choff
質問者

お礼

ホントだ!スルドイですね…☆ 配列定義でfortranは1から始まるのにcは0から始まるなんて、馴染みにくいです涙 単純にまだまだ甘ちゃんなだけですが。どうもありがとうございます☆

すると、全ての回答が全文表示されます。
  • dra2jp
  • ベストアンサー率25% (18/72)
回答No.6

#5様、補足ありがとうございます。 CでもJavaと同じような書き方が出来るんですね。 C++で以下のような書き方できる事は存じていたんですけど、 #include <iostream> int main() { cout << "私は" << 43 << "才です。" << endl; } Cで書くと × printf("name is "David" .\n"); ○ printf("name is " "David.\n"); 例はこうでないといけなかったですね。 C++でサンプルのようなプログラムが書けることも覚えておくと便利ですよ。 >分かりやすく説明するためにあえてnullが入っていると言うことにしたのかもしれませんが、 いえ、本気でNULLだと思ってました(汗) よく考えたら、なにかデータは入っていなければならないはずですから、¥0がはいっていて当然のようにも考えられますね。 本当に\0が代入されているかどうか確認しました。 こちらのサンプルで確認できます。 #include <stdio.h> int main(void) { int i; char name[10] = "David"; char name2[5] ={'P','o','l','e','e'}; printf("\\0未代入\nname2=%s\n\n",name2); name2[4]=name[9]; printf("\\0既代入\nname2=%s\n\n",name2); return 0; } もしかしてヌルとぞくに言うのはnull文字が入っているからなのでしょうかね?

すると、全ての回答が全文表示されます。
  • Werner
  • ベストアンサー率53% (395/735)
回答No.5

本題とはあまり関係ないですが。 >>#4 > ○ printf("seikai\n"); > × printf("seik" "ai\n"); > 2回目にダブルコーテーションを書いた時点で文字列はそこまでだと認識されます。 > ×の例の場合、文字列は seik だと認識されます。 ダブルコーテーションのエスケープの必要性はその通りなのですが、 ×の例の場合 seikまでしか認識されないというのはちょっと違います。 連続する文字列定数はコンパイル時に結合され1つの文字列として扱われます。 つまり、「"seikai\n"」は「"seik" "ai\n"」と書いてもかまわないです。 これは長い文字列を複数行に分けて書くとき便利なのですが 意外と知っている人は少ないのかな。 >>#5 > name[0][6] == (null) 何もないという意味です。 分かりやすく説明するためにあえてnullが入っていると言うことにしたのかもしれませんが、 実際には配列の初期化時に値が明示的に指定されなかった要素は0で初期化されます。 (今回の場合char型なので'\0'が入っているともいえます。) http://rec.ncos.co.jp/cgi-bin/e000.cgi?405 http://homepage3.nifty.com/mmgames/c_guide/13-02.html

chee-choff
質問者

お礼

ふおお…今最近買った参考書を見直したらprintf関数の解説で同じようなことが書いてありました。線引いてあるのに覚えれていない汗 こんな細かいこと覚えてても使うか!とかたまに思ったりしますが、やはり首を突っ込めば突っ込むほどこんな知識もどんどん必要になっていくんですねぇ…ちょとゾッとします笑 ありがとうございますm(_ _)m

すると、全ての回答が全文表示されます。
  • dra2jp
  • ベストアンサー率25% (18/72)
回答No.3

こんばんは。以前書き込みをしましたdra2jpです。 このサイトは個人的な通信が禁止されていて困ったものですねw さて、今回の質問について解答しましょう。 前にも言いましたけど name[99] = "David"; では文字列代入は出来ません。 name[99]はアドレスを示すのであって・・、 前回散々書いたのでアドレスについては前の質問の回答をお読みください。 代入はstrcpyをお使いください。 strcpy(name[99],"David"); ですね。 そしてこのプログラムでおかしいところはもう一つ。 printf("The name ""David"" is included in this list.\n"); このprintf文です。 プリント文の出力する文字列はダブルコーテーション「"」から1つのダブルコーテーションで囲まれた中の文字列でなければなりません。 つまり ○ printf("seikai\n"); × printf("seik" "ai\n"); 2回目にダブルコーテーションを書いた時点で文字列はそこまでだと認識されます。 ×の例の場合、文字列は seik だと認識されます。 ですので、単に書いただけでは出力画面にダブルコーテーションを表す事が出来ません。 もしも出力画面にダブルコーテーションを現したいのでしたらエスケープシーケンスというものを使います。 ○ printf("seik\" \"ai\n"); × printf("seik" "ai\n"); わかりますか?\(円マークまたはバックスラッシュ)をダブルコーテーションの前に書いて 「このダブルコーテーションは文字列の終わりを意味するのではなく画面に出力したい文字としてここに書いてありますよ」 という事を示してやらなければなりません。 プログラムというものは聞いていただけじゃなんのこっちゃわかりませんから 百聞は一見にしかずです。こちらを実行されてみてください。 #include<stdio.h> int main(){ printf("The name \"\"David\"\" is included in this list.\n"); return 0; } 希望通りの出力画面になったと思います。 つまりこのプログラムはname[99]の代入文とprintf文を直せば正常に動きます。 もしも動かなかった場合また聞いてください。 そして投稿者様も気にしていただいていた、以前私がわかったらお伝えするといっていた事についてですが。 遅くなってごめんなさい。 どうしてあぁなるのかわかりました。 長くなったのでその回答は#4に記載します。

すると、全ての回答が全文表示されます。
  • ko_kinta
  • ベストアンサー率39% (43/109)
回答No.2

文字列の代入方法については前回の質問QNo.2277663で、ANo.2のBLUEPIXYさんが簡潔に回答してらっしゃいますよ。 基本的なことですから、きちんと把握しておきましょう。 以下BLUEPIXYさんの回答です。 >strcpy(name[0],"David"); >とかstrncpy を使いましょう > >または、不定長の文字列を扱う場合は、 >ポインタの配列 >char *name[100]; >にして >name[0]=strdup("David"); >とか >malloc してstrcpy

chee-choff
質問者

お礼

あああ本当だ…すみません、おっしゃる通りです。

すると、全ての回答が全文表示されます。
  • funaho
  • ベストアンサー率58% (43/74)
回答No.1

name[99] = "David"; を strcmp(name[99], "David"); に変更すると出来ます。

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

関連するQ&A