- ベストアンサー
関数への構造体の配列の渡し方<c言語初心者>
- 関数への構造体の配列の渡し方で理解できない点があるため、質問させていただきます。
- 以下は、関数への構造体の配列の渡し方についてのスクリプトです。
- 構造体の配列をinput関数やoutput関数に渡す際にエラーが発生しており、解決できません。皆様のお力をお貸しいただけますでしょうか。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
もちろん void input(property *data); にして int main(void){ property data[3]; input(data); が最も普通 (まあ大きさも渡すと思うけど) です>#7. 配列の各要素は連続して配置されますから, &data[0] = data, &data[1] = &data[0]+1 = data+1, &data[2] = &data[0]+2 = data+2 が (意味のある限りにおいて) 常に成り立ちます. つまり「ポインタの配列」を使う必要性はほとんどありません. しいて言えば「複数の配列のデータを扱うとき」には意味はあるけど, そのような状況があまり想像できない. あと void input(property (*data)[3]); とすると「大きさ 3 の配列」は渡せるけどその他の大きさの配列は渡せなくなります. ただ, 例えば property data[4][3]; とかいう 2次元配列を input(data); のように渡す (ここで &data としちゃうとさらに面倒です) と上のような形が発生します. この 2次元配列との絡みで「3」が省略できない.
その他の回答 (7)
- Wr5
- ベストアンサー率53% (2173/4061)
>でもそうすると、配列の要素全てのアドレスをinput関数に渡したいとなると、Wr5さんのおっしゃるように、あたらしくpdata[3]のようなアドレスを代入する配列を新しくつくらないといけないということになりますね。 今回の場合だったら… void input(property *data); にして int main(void){ property data[3]; input(data); でも可能かと。 ただし、input()では配列の要素がいくつあるのか(そもそも配列なのか)は知る方法がありませんけど。 #6に描かれている内容からすると… void input(property (*data)[3]); で3個だと…やれるのですかね。 こういう書き方はやったことないので不明ですが。 呼び出し側が3つ以外の配列の場合に面倒になりそうですな…。 # そんな訳で、そういうパターンだと引数1つ追加して個数も渡すようにすることが多いです。
お礼
仰る通りにやってみたら、コンパイル通りました。 void input(property *data);------(1) と void input(property data[]); と void input(property data[3]); って等価なんですね。 (上記のどの表記にせよ、input関数に渡すのは配列の先頭のアドレスに変わりはない) でも、(1)の表記はポインタを意識してしまうので紛らわしいですね。(アドレスを渡すのだからポインタっぽくていいと言えばいいのですが…。) 御回答ありがとうございました。
- Tacosan
- ベストアンサー率23% (3656/15482)
型はなんでも同じなんで int で書いてみる. int a; → a は int int a[3]; → a は int×3 の配列 int *a; → a は int へのポインタ int *a[3]; → a は (int へのポインタ)×3 の配列 int (*a)[3]; → a は (int×3 の配列) へのポインタ int a(); → a は引数不明の int を返す関数 int *a(); → a は引数不明の (int へのポインタ) を返す関数 int (*a)(); → a は (引数不明の int を返す関数) へのポインタ int (*a[3])(); → a は ((引数不明の int を返す関数) へのポインタ)×3 の配列
お礼
御回答ありがとうございます。 int (*a)[3]; → a は (int×3 の配列) へのポインタ の解釈はわかりましたが、 int *a[3]; → a は (int へのポインタ)×3 の配列 の説明がイマイチ理解できていないため、もう少し考えてわからなかったら、別件で新しく質問させて頂きます。
- Tacosan
- ベストアンサー率23% (3656/15482)
「配列を関数に渡す」とき, 配列名に & を付けるのはたいてい間違いだと思っていいです. 質問文では「input関数やoutput関数に渡すとき」だけを意識していますが, 実は scanf でも警告が出ています. ちなみに今の例でど~しても input(&data); と渡したければ void input(property (*data)[3]); というプロトタイプが必要です.
お礼
仰る通り、 void input(property (*data)[3]) と記述すると見事にコンパイルが通りました。 *data[3]と書くと、"data[3]という要素のポインタ"となり、 (*data)[3]と書くと、"要素数3つのdataのポインタ"となるのかな、と私なりに解釈しました。 しかし、 void input(property (*data)[]) のように、要素数を書かないと通りませんでした。不思議です。 しばらく、このことについて調べたり、考えてみようかと思います。 御回答ありがとうございました。
- Wr5
- ベストアンサー率53% (2173/4061)
>ミイラ取りがミイラになってますよ>#1. コピペで適当に直しただけでしたから…。 って言い訳も微妙に見苦しいな。
- Tacosan
- ベストアンサー率23% (3656/15482)
おっととと. ミイラ取りがミイラになってますよ>#1. # & が余計
- Tacosan
- ベストアンサー率23% (3656/15482)
余談. 構造体じゃなくって int の配列だったらどう渡す?
お礼
intの配列の場合を考えようと、試しに以下のようなプログラムを作りました。 #include <stdio.h> int getsum(int *data[]); int main(void){ int total, array[5]={13,83,19,25,49}; total=getsum(&array); printf("%d\n",total); return 0; } int getsum(int *data[]){ int i,sum; for(i=0;i<5;i++){ sum+=*data[i]; } return sum; } すると、同じようにコンパイルエラーが出ました。 いろいろ調べてみると、(Wr5様への御礼にも書きましたが、)配列を渡す、というのは、配列の先頭のアドレスを渡す、ということで、上記のプログラムでgetsum(&array)として渡すのは間違いで、&記号はいらないわけですね。そして受け取り側も配列の型で受け取ると。 私は無理に配列の要素全てのポインタを渡そうとしていましたが、関数への配列の受け渡しはそもそもアドレスのやりとりですから、ポインタを使う意味はないですね。一応疑問は解決致しました。 ヒントといいますか、的確な道を示して下さり勉強になりました。 もし上述の私の理解に間違いがございましたら、よろしければご指摘頂ければ幸いです。 御回答ありがとうございました。
- Wr5
- ベストアンサー率53% (2173/4061)
>void input(property *data[]); 「property型へのポインタ」の配列であって、 「property型の配列の先頭要素へのポインタ」ではない…ですよね? 単純に void input(property *data); にするべきなのではありませんか? # 実際には、配列のサイズも渡した方が汎用性(?)は上がりますが。 void input(property *data[]); でやりたいのならば…… int main(void){ property data[3]; property *pdata[3]; pdata[0] = &data[0]; pdata[1] = &data[1]; pdata[2] = &data[2]; input(&pdata); かと。
お礼
Wr5様とTacosan様の御回答から、 #include <stdio.h> typedef struct{ char name[64]; int age; }property; void input(property *data[]); void output(property *data[]); int main(void){ property data[3]; property *pdata[3]; pdata[0]=&data[0]; pdata[1]=&data[1]; pdata[2]=&data[2]; printf("Input data of three people.\n"); input(pdata); output(pdata); return 0; } void input(property *data[]){ int i; for(i=0;i<3;i++){ printf("%d banme\n",i+1); printf("name:"); scanf("%s",data[i]->name); printf(" age:"); scanf("%3d",&data[i]->age); } return; } void output(property *data[]){ int i; for(i=0;i<3;i++){ printf("%d banme\n",i+1); printf("name:%s\n",data[i]->name); printf("age :%3d\n",data[i]->age); } return; } と書いてみたら、無事コンパイルできました。 こうしてみると、input関数(引数:ポインタ)に渡すときに構造体の配列を input(&data)として渡すことはできないのですね。 もしかして、配列は先頭のアドレスを渡すから、&は必要ないということですか。なるほど。 でもそうすると、配列の要素全てのアドレスをinput関数に渡したいとなると、Wr5さんのおっしゃるように、あたらしくpdata[3]のようなアドレスを代入する配列を新しくつくらないといけないということになりますね。 もし間違っていましたらご指摘下されば幸いです。 御回答ありがとうございました!
お礼
配列はそもそもポインタのようなものだから、わざわざ配列をポインタにする意味がないとわかったのは、今回の大きな収穫でした。 >void input(property (*data)[3]); >とすると「大きさ 3 の配列」は渡せるけどその他の大きさの配列は渡せなくなります. ただ, 例えば >property data[4][3]; >とかいう 2次元配列を >input(data); >のように渡す (ここで &data としちゃうとさらに面倒です) と上のような形が発生します. この 2次元 >配列との絡みで「3」が省略できない. すみません、ちょっと難しいです。 今の私には少しレベルが高いようなので、もう少し勉強して、わからなかったらまた質問しに参ります。 御回答ありがとうございました。