こんにちは、関数への構造体の配列の渡し方で理解できない点があるため、質問させていただきます。
以下がスクリプトになります。3人の名前と年齢をinput関数で入力し、それらのデータをoutput関数で出力するのが目的です。
#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];
printf("Input data of three people.\n");
input(&data);
output(&data);
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;
}
コンパイル時のエラーメッセージは以下のようになりました。(ファイル名はstructure5.c)
structure5.c: In function ‘main’:
structure5.c:14:2: warning: passing argument 1 of ‘input’ from incompatible pointer type [enabled by default]
input(&data);
^
structure5.c:8:6: note: expected ‘struct property **’ but argument is of type ‘struct property (*)[3]’
void input(property *data[]);
^
structure5.c:15:2: warning: passing argument 1 of ‘output’ from incompatible pointer type [enabled by default]
output(&data);
^
structure5.c:9:6: note: expected ‘struct property **’ but argument is of type ‘struct property (*)[3]’
void output(property *data[]);
^
structure5.c: In function ‘input’:
structure5.c:24:3: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[64]’ [-Wformat=]
scanf("%s",&data[i]->name);
^
構造体の配列をinput関数やoutput関数に渡すときにエラーが発生しているようなのですが、自分で調べても解決できなかったため、質問させて頂きます。
皆様のお知恵を貸してください。なおプログラミング言語自体初心者のため、できる限りわかりやすいお言葉でご教授願います。よろしくお願い致します。
No.1
- 回答日時:
>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]のようなアドレスを代入する配列を新しくつくらないといけないということになりますね。
もし間違っていましたらご指摘下されば幸いです。
御回答ありがとうございました!
No.2
- 回答日時:
余談.
構造体じゃなくって 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)として渡すのは間違いで、&記号はいらないわけですね。そして受け取り側も配列の型で受け取ると。
私は無理に配列の要素全てのポインタを渡そうとしていましたが、関数への配列の受け渡しはそもそもアドレスのやりとりですから、ポインタを使う意味はないですね。一応疑問は解決致しました。
ヒントといいますか、的確な道を示して下さり勉強になりました。
もし上述の私の理解に間違いがございましたら、よろしければご指摘頂ければ幸いです。
御回答ありがとうございました。
No.5
- 回答日時:
「配列を関数に渡す」とき, 配列名に & を付けるのはたいてい間違いだと思っていいです. 質問文では「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)[])
のように、要素数を書かないと通りませんでした。不思議です。
しばらく、このことについて調べたり、考えてみようかと思います。
御回答ありがとうございました。
No.6
- 回答日時:
型はなんでも同じなんで 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 の配列
の説明がイマイチ理解できていないため、もう少し考えてわからなかったら、別件で新しく質問させて頂きます。
No.7
- 回答日時:
>でもそうすると、配列の要素全てのアドレスを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)の表記はポインタを意識してしまうので紛らわしいですね。(アドレスを渡すのだからポインタっぽくていいと言えばいいのですが…。)
御回答ありがとうございました。
No.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」が省略できない.
配列はそもそもポインタのようなものだから、わざわざ配列をポインタにする意味がないとわかったのは、今回の大きな収穫でした。
>void input(property (*data)[3]);
>とすると「大きさ 3 の配列」は渡せるけどその他の大きさの配列は渡せなくなります. ただ, 例えば
>property data[4][3];
>とかいう 2次元配列を
>input(data);
>のように渡す (ここで &data としちゃうとさらに面倒です) と上のような形が発生します. この 2次元
>配列との絡みで「3」が省略できない.
すみません、ちょっと難しいです。
今の私には少しレベルが高いようなので、もう少し勉強して、わからなかったらまた質問しに参ります。
御回答ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- PHP PHPでユーザー情報を入力して簡易ログイン機能をつくってみたのですが 1 2023/05/29 08:51
- PHP PHPで画像の渡しが上手く行きません。 1 2023/02/02 09:39
- PHP PHPでCookieを使った訪問回数について 1 2023/05/28 14:10
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# C言語初心者 構造体 課題について 1 2023/03/10 19:30
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
c言語
-
関数から配列を返すには?
-
100の階乗のプログラムって
-
ループカウンタを使用せず、配...
-
要素数・要素の値が未定の配列...
-
コンボボックスでデフォルト値...
-
配列のアドレス部
-
RAW画像高速表示について
-
2番目の最大値を求める
-
define で 配列
-
配列の要素数に変数を入れたい...
-
C言語の課題が出たのですが自力...
-
背景差分法における正規化距離
-
.NET C++で、構造体の配列をnew...
-
配列の配列をmemcpyやmemcmpし...
-
[C++/CLI]マネージコードで配列...
-
円周率を求めるプログラム
-
シマウマの模様を変えるプログラム
-
C#で構造体の配列を持った構造...
-
C言語についてです 5人のテスト...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
関数から配列を返すには?
-
配列の要素数に変数を入れたい...
-
define で 配列
-
c言語
-
C#で構造体の配列を持った構造...
-
構造体のextern方法
-
C言語において、 配列要素をひ...
-
C#でのフィボナッチ数列
-
C言語の2次元配列 容量が大き...
-
C#で配列が空かを判定するには?
-
配列のアドレス部
-
char型配列をint型に代入するには
-
2番目の最大値を求める
-
C言語の課題が出たのですが自力...
-
C言語についてです 5人のテスト...
-
C言語から質問です。
-
C言語 ファイルの指定された行...
-
c言語 構造体
-
コンボボックスでデフォルト値...
-
MFCのCArrayを使った二次元配列
おすすめ情報