「これはヤバかったな」という遅刻エピソード

はじめまして、工学系の大学生です。C言語について質問です。

文字単体をデータとして配列を作るなら、例えば

char [100];

で構わないと思うのですが、文字列を1つのデータとして配列を作る場合はどうしたらいいのでしょうか?僕が思いついたのは2次元配列を用いて1つの次元をデータ数、もう1つの次元を各データの最大文字数として例えば

char name[100][20];

のように定義して、

name[0][]=David;
name[1][]=Mathew;

などととすれば、printf関数でname[0]を出力したらDavidが出てくれるかと思ったのですが、エラーがたくさん出てきてしまいました。


もしよろしければどなたか教えていただけないでしょうか?どうぞよろしくおねがいしますm(_ _)m

A 回答 (7件)

引き続き回答します。


なるほど、ちょっと配列の根本を見直してみましょう。

if(name[i][10]==comp[5])

は何をしようとしているかはわかります。
きっと文字列比較をしたいのでしょう。
しかし、実際希望の処理は行われていません。では何が行われているか考えてみてください。

char name[5][10]={"David","Mathew","Linda","Pole","Thomas"};
char comp[5]="Pole";
とすると何が格納されるのでしょうか。
DavidとPoleについて考えます。

name[0][0]には D
name[0][1]には a
name[0][2]には v
name[0][3]には i
name[0][4]には d
name[0][5]には \0
それ以降には何も入っていません。
つまり(null)です。
そして
comp[0]には P
comp[1]には o
comp[2]には l
comp[3]には e
comp[4]には \0
それ以降は何も無し。
つまり(null)です。

上記の通り
name[0][10]には何も入っていません。(null)です。
comp[5]にも何も入っていません。(null)です。
つまり
if(name[i][10]==comp[5])
は何も入っていない配列要素の1つと
何も入っていない配列要素の1つを比較しているのです。

なのでPoleが一致したからif文内のprint文が実行されたわけではありません。
比べたものはnullとnull。つまり何もないものと何もないものを比較して何もないという意味では一緒だと
処理されたのでしょう。
つまりその考え方なら、毎回一致するはずですが、なぜfor文の最後だけ一致したのかはわかりません。
後で、何故なったのか実行しながら考えて見ます。


ところで、配列について見直します。
配列は要素数を示さず書くと何を示しているか知っていますか?
その配列が始まる先頭アドレスです。
アドレスとかポインタとか言うと入門者さんは頭を抱えてしまうと思いますが、
難しく考えないで下さい。

全てのデータはデータのアドレスで管理されています。
22234番地にこのデータ、45342番地にこのデータと言う具合です。
配列によって今10要素を宣言すると連続した番地にデータが用意されます。
つまり
231~241番地に用意されるといった具合です。

name[0][0]と指定すると格納した値"D"が示されますが
name[0]と指定すると、その配列が始まる先頭のアドレス、つまりこの場合231番地が示されるわけです。
文字列比較をする場合はこの番地(ポインタといいます)で比較しなければなりません。
(そうではなくて文字列の2番目の要素と文字列の3番目の要素を比較すると言った具合に特定の文字を比較するのでしたら要素数を指定する必要があります。)

if(name[0] == "David")

とすると希望の比較が出来ると思うでしょう。
前述どおりname[0]は文字列の先頭アドレスを示してますよね。
ここのアドレスのデータからデータの値がDavidですと。
一方、比較の右辺この場合の"David"は見た目わかりませんが、この""の部分も自然とポインタの比較をしていることになります。
文字列の中身を比較してくれるわけではありません。

つまり文字列の中身を比較する関数を利用して比較する必要があります。
strcmpという関数があります。
strcmp(name[0], "David");
この返り値が0なら文字列は同じ、
0以外なら文字列は違います。
つまり文字列を比較したい時は

if(strcmp(name[0], "David") == 0)
 printf("2つの文字列は同じ\n");

こんな感じになります。
つまりcompと比較したいのならばcomp

if(strcmp(name[0], comp) == 0)
 printf("2つの文字列は同じ\n");

こうなりますね。
では希望通りの処理が出来るようにプログラムを書いてみます。

#include <stdio.h>
#include <string.h>
int main(void)
{
int i;
char name[5][10]={"David","Mathew","Linda","Pole","Thomas"};
char comp[5]="Pole";

for(i=0;i<=4;i++){
if(strcmp(name[i],comp)==0){
printf("Poleは配列 name の中にあります。\n");
printf("そのデータナンバーは %d です。\n",i);
printf("name[%d]=%s と comp=%s を比較した時に確認しました。\n",i,name[i],comp);
}
}
return 0;
}

難しい事は何も書いてありませんからゆっくり見てください。
また、何か分からない事があったら何でも聞いてください。
    • good
    • 0
この回答へのお礼

おっしゃる通りポインタやらアドレスやらで頭痛を起こす人間です(笑)
でもdra2jpさんの解説はとてもわかりやすいので質問した部分に関してはおおかた理解できました☆そして配列の考え方のみならずアドレスの基本まで教えていただけるとは…多謝多謝ですm(__)m

お礼日時:2006/07/16 16:24

#2>for文を使って反復命令として実行できないかということです。


C言語では動的に変数名を合成・作成して変数にアクセスするというようなことができないので、配列などに格納してfor で回すようにします。
変数名を別につける理由がないなら、for で配列で使うような用途であれば、最初から配列にします。
また、C言語では、変数名に「-」は、使えなかったと思います。(演算子に解釈されるため、例えばname-a[n] は、name - a[n]と解釈されます。)
----------------------------------------------------------------
#include <stdio.h>
#include <string.h>

int main(void){
char name_a[]="David";
char name_b[]="Mathew";
char name_c[]="Smith";
char name_x[]="Smith";

char *name[3];
int i;

name[0]=name_a;
name[1]=name_b;
name[2]=name_c;

for(i=0;i<=2;i++){
if(strcmp(name[i], name_x)==0){
printf("This list has same data to name-x.");
break;
}
}
return 0;
}
    • good
    • 0
この回答へのお礼

なるほど、変数名に数値を勝手に入れて動かしたりはできないのですね。イメージ的に動きそうだったのですが…プログラムで文法を気にせず自分のイメージなどに従うなど言語道断ですね(汗)なんだか主題以外に色々な知識を頂戴できました。ありがとうございます☆

お礼日時:2006/07/16 16:47

この回答は下を読んだ後、補足として


お読みください。

ちょっと気になりましたが、配列について色々誤解されていらっしゃる部分が多いようですので、
この回答を見た後で、配列について参考書やウェブなどでおさらいして置いてください。

printf("name[0][10] = %s.\n",name[0]);

の部分で気になりました。
name[0][10]の部分には文字1文字しかはいりません。
配列要素の1つにすぎません。
name[0]にはそこから始まる文字列の先頭アドレスが示されています。

つまり
char name[1][11] ="01234567890";
と宣言した時には
name[0][10]には0が入っています。

printf("name[0][10] = %c.\n",name[0][10]);

の表示は
0
です。

printf("name[0][10] = %s.\n",name[0]);

と書いていらっしゃるので、一応文字列はname[0]で示す事をしっていらっしゃるとは思いますが・・。

char name[8]="David";

と宣言した時、文字列Davidを示したい時は[]無しで
name
とだけで示しますよね?

この配列の中身(配列要素)はなんでしょうか。

name[0]には D
name[1]には a
name[2]には v
name[3]には i
name[4]には d
name[5]には \0
name[6]には null(何も無し)
name[7]には null(何も無し)

そして

name には 文字列の先頭アドレス(私のパソコンの場合1245060)が格納されています。
前にも書いた通り、配列要素のアドレスは連続しています。

このことを確認するために次のようなプログラムを実行して確認してください。

#include <stdio.h>

int main(void)
{
int i;
char name[8]="David";

printf(" name[0]=%c\n name[1]=%c\n name[2]=%c\n name[3]=%c\n name[4]=%c\n name[5]=%c\n name[6]=%c\n name[7]=%c\n name =%d\n\n",name[0],name[1],name[2],name[3],name[4],name[5],name[6],name[7],name);
printf(" &name[0]=%d\n &name[1]=%d\n &name[2]=%d\n &name[3]=%d\n &name[4]=%d\n &name[5]=%d\n &name[6]=%d\n &name[7]=%d\n name =%d\n",&name[0],&name[1],&name[2],&name[3],&name[4],&name[5],&name[6],&name[7],name);
return 0;
}

実行してみればわかると思いますが
配列要素のそれぞれには1つずつの文字が
それが集合して1つの文字列を成し、
nameはその文字列の先頭アドレスを示す。
つまり
name[0]
のアドレスと
name
のアドレスは同じになると。

わかりましたか?
まだ、アドレスについて勉強した事がなかったら少し難しかったかもしれませんね。
配列はポインタ無しには説明できないので、
もし今理解できなかったらポインタを勉強してから
また後々この回答を見直してみてください。
    • good
    • 0
この回答へのお礼

載せて頂いたプログラム実行しました!!データは全部アドレスを持ってて…と言われてもなかなか実感が湧かないものですが、それを一度でも実際に表示させてみると「なるほーそゆことか!」ってわかるところが凄いです。

がしかし1つ前の回答でdra2jpさんに僕のへっぽこプログラムの謎に対して「わかったらまた言う」と言ってもらったにもかかわらず回答を締め切ってしまいました~すみません(T-T)もしよろしければ是非続きを聞きたかったのですが…締め切りのキャンセルとかできないんでしょうか??それかもう一回トピを立てるしかないのでしょうか…すみません余計な所で迷惑かけてしまって。。

お礼日時:2006/07/16 16:40

あ、先ほどの回答の一番最後の正解例に要素数6を書き忘れました。


name[1][]
このように要素数を省略できるのは宣言時だけです。
しかしこのような省略はいけません。

#include<stdio.h>
#include<string.h>

int main(){
char name[4][]={"David","Smith","",""};
strcpy(name[2],"suzuki");
strcpy(name[3],"hanako");

printf("[0]=%s\n[1]=%s\n[2]=%s\n[3]=%s\n",name[0],name[1],name[2],name[3]);
return 0;
}
name[2]とname[3]にはいくつ要素をいれていいかわからず、エラーになります。

ですからこの場合

#include<stdio.h>
#include<string.h>

int main(){
char name[4][20]={"David","Smith","",""};
strcpy(name[2],"suzuki");
strcpy(name[3],"hanako");

printf("[0]=%s\n[1]=%s\n[2]=%s\n[3]=%s\n",name[0],name[1],name[2],name[3]);
return 0;
}
このようにしましょう。
要素数は多めに宣言するのが基本です。
    • good
    • 0
この回答へのお礼

回答ありがとうございます!!
No.3の回答がかなり参考になりました☆そして作りたいプログラムも完成しました~ホントにありがとございますm(__)m
それでお言葉に甘えて追加で質問させていただきたいのですが、No.2の回答でも載せました「人物名リストの中から必要な要素を見つける」というプログラムを以下のように書いたのですが、

#include <stdio.h>
int main(void)
{
int i;
char name[5][10]={"David","Mathew","Linda","Pole","Thomas"};
char comp[5]="Pole";

printf("name[0][10] = %s.\n",name[0]);
printf("name[3][10] = %s.\n\n",name[3]);


for(i=0;i<=4;i++){
if(name[i][10]==comp[5]){
printf("Pole is included in this list.\n");
printf("Data number is %d.",i);
}
}

return 0;
}

これを実行すると次のような結果が出ててしまうんです。

name[0][10] = David.
name[3][10] = Pole.

Pole is included in this list.
Data number is 4.

つまり、一致した(if文が成立した)時のiの値は3なのに、その直後にiを出力するとなぜか4が出てきてるんです。

これは一体どうしてなのでしょうか?どうかご回答よろしくお願いします。m(_ _)m

お礼日時:2006/07/16 01:05

#1,#2さんのおっしゃる事ももっともですが、


ちょっと質問者さんの聞きたい事とずばり同じではないと思うので、回答を書きますね。

まず、初期化同時に文字列を格納できるのは宣言時だけです。

それに文字列はダブルコーテーション「”」で囲みましょう。

例を出します。

int main(){
char name[1][]="David";

return 0;
}

↑これは正解です。宣言時に代入していますから。

int main(){
char name[1][6]="David";

return 0;
}

↑これも正解。なぜ6なのかは文字列の終端に終端記号(ヌル文字)\0 が入るからです。


int main(){
char name[2][]={"David","Mathew"};

return 0;
}

↑これも正解で


int main(){
char name[2][7]={"David","Mathew"};

return 0;
}

↑これも正解です。

しかし以下これは違います。

int main(){
char name[1][];
name[0][]="David";

return 0;
}

int main(){
char name[1][6];
name[1]="David";

return 0;
}

int main(){
char name[1][];
name[1]="David";

return 0;
}

↑これらは全て間違いです。宣言時にしか初期化として格納できません。
もし宣言の後にだいにゅうしたいのでしたら
代入するための関数を使います。
一番最初に
#include<string.h>
を書いて

int main(){
char name[1][];
strcpy(name[0],"David");

return 0;
}

と書きましょう。
strcpy();関数は文字列を代入する関数です。
これを使用する場合はstring.hを最初にインクルードする必要があります。

まだ解らないところがありましたら再度きいてください。
    • good
    • 0

strcpy(name[0],"David");


とかstrncpy を使いましょう

または、不定長の文字列を扱う場合は、
ポインタの配列
char *name[100];
にして
name[0]=strdup("David");
とか
malloc してstrcpy
    • good
    • 0
この回答へのお礼

回答ありがとうございます。すみませんが、先ほど言い忘れてしまったので下の方に対してもここで申し上げます、回答ありがとうございます。
えと、配列に文字列を代入(コピーと言うらしいですね)する方法は分かったのですが、それを複数個作成した場合にfor関数で一括処理できるようにはできないでしょうか?
例えば、
char name-a[]="David";
char name-b[]="Mathew";
char name-c[]="Smith";
という3つの文字列配列があって、これを
char name-x[]="Smith";
と同じ文字列であるかを判別する際に、
for(i=0;i<=2;i++){
if(XXXX==name-x[]){
printf("This list has same data to name-x.");
}
}
とfor文を使って反復命令として実行できないかということです。

(注1)XXXXの所にはname-aからname-cが何らかの形でi=0,1,2によって区別されたものが入る←理想です
(注2)printfの内容はテキトーに考えたのでツッコまないでください。文法もしかりです泣

最初の質問が非常にわかりにくかったようで、すみません。どうかご一考お願いします。

お礼日時:2006/07/16 00:01

第1に文字列の場合は数字と違って、


name[6]="David";
のように配列の1要素に対して1文字の代入でなければなりません。NULL文字も忘れずに。

あと、変数に文字列を代入したいのであれば

"David"

のようにダブルコーテーションで文字列と認識させなければなりません。
    • good
    • 0
この回答へのお礼

ああーすいません、ダブルクオテーションいりますね。頓珍漢な例を出してしまって申し訳ないです。。すみません。

お礼日時:2006/07/15 23:15

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


おすすめ情報