こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

関数の仮引数は宣言か式か

”関数の仮引数の宣言”は”変数の宣言・定義”と同じように”宣言”と明確に考えるべきなのか”関数の仮引数”を”式”と考えて良いのかという問題です。
私が迷ってしまったのは"配列を仮引数"にとった例です。
規則として
”int a[ ] が int *a と同じ意味になるのは、唯一、関数の仮引数の宣言のばあいだけである”という規則がありますが、これは”関数の仮引数の宣言”は変数の宣言・定義”と同じように”宣言”と明確に考えている例だとおもいます。
int a[ ] が int *a と型名 変数名(引数名)と宣言の形をとっているので当然だと思いますが、一方

配列は、式の中で「先頭へのポインタ」によみかえられる。
              ↓
関数の引数は式なので、配列は「先頭へのポインタ」に読み変えられる←引数部分を”宣言”ではなく”式”と捉えてる←ここが私の迷っているところ
              ↓
よって、関数に渡ってくるのは、結局はつねにポインタだ。
という説明もあります。

私の今までの理解ではc言語では”宣言の部分”と”式”の部分は明確に区別されるものと考えていました。”宣言部分の初期化の="と”代入の=”とは明確に区別されていました。
それと同じように”関数の仮引数の宣言”は”宣言部分”と捕らえるのか”式”ととらえるのか
”宣言”と”式”が私の頭の中混乱しています。
宜しくお願いします。

投稿日時 - 2008-06-15 06:35:20

QNo.4101762

すぐに回答ほしいです

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

a[] 自体、という記述を見て、疑問点が分かったように思います。
[] は変数型の一部と見るべきなので、変数にあたるのは a[] ではなく a なのだと考えてください。
a は変数ですからこれ自体は式に間違いありません。今回の場合には int へのポインタ型の変数 a ということですね。

void Func(int ai[]) {} を例として考えてみます。

変数型に名前をつける typedef をご存知であろうと思いますが、これを使って上記関数の引数の型に ai_type(名前は任意ですが)という名前をつけるとすると、

typedef int ai_type[];

と書けますね。この型名を使って上記の関数は次のように書けます。

void Func(ai_type ai) {}

このように書くと、ai が変数であり、[] は変数型 ai_type の一部なのだということが納得できると思いますが、いかがですか。

上記のような観点で見ると、a[] は式と考えて良いかという質問は、「変数型」と「変数の識別子」の組み合わせは式ですかというのに近く、例えば int c は式ですかというのに近いので、これは式とはいえないということになります。
c は式ですが、int c は式ではないですよね。
同様に a は式ですが a[] は式ではなく、しかもこの場合の [] は ai_type という型を構成する要素である、ということになります。

投稿日時 - 2008-06-15 14:53:12

お礼

私の再質問のご回答の最初で”a[] 自体、という記述を見て、疑問点が分かったように思います。”とあり、それに対し、詳しく説明していただき、疑問点は良く理解できました。大変有難うございました。

投稿日時 - 2008-06-16 04:40:01

ANo.4

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.3

plh

たぶん誤解されることは無いと思うのですが、ANo.002 で、「式というと 1*2+3 も式ですが、関数定義として void Func(1*2+3) {} はありえませんから少なくとも関数の"""仮引数は式とは言えないですね"""。」と書きましたが、この意味は、関数の仮引数部分に任意の式を書くことはできないという意味であり、void Func(int* pi) {} の仮引数 pi 自体はもちろん式と言えますので、念のため。

投稿日時 - 2008-06-15 10:04:34

補足

1.void Func(int* pi) の仮引数 pi 自体
2.void Func(int a[ ])の仮引数a[ ]自体
1 2のpi 自体 a[ ]自体は式と考えてよいのでしょうか。
そこが私の疑問点の核心ですので補足を投稿しました。
宜しく願います。

投稿日時 - 2008-06-15 13:14:29

ANo.2

plh

式というと 1*2+3 も式ですが、関数定義として void Func(1*2+3) {} はありえませんから少なくとも関数の仮引数は式とは言えないですね。

関数のプロトタイプ宣言、例えば void Func(int*); の引数は、引数型を宣言しているだけなので、宣言でしょう。
関数の定義、例えば void Func(int* pi) {} の引数は、引数型が決まると同時に、引数(=変数)の実体ができるという意味で、定義でしょう。

問題の配列の場合ですが、関数のプロトタイプ宣言の void Func(int ai[]); と void Func(int* pi); は全く同じ意味になると思います。

関数のプロトタイプ宣言の引数の名前は省略できるので、上記は void Func(int[]); と void Func(int*); と書いても良いわけです。
これらはどちらも、引数は int を指すポインタだと言っていることになります。
関数の定義では、引数を本体内部で使うのならば名前が必要なので、引数並びに ai とか pi とかいう名前が必要になります。
上記のように、配列を関数に値渡しする場合には、引数は配列の先頭アドレスで初期されれたポインタが渡ります。
引数の実体がポインタなので、この場合、配列の大きさは無視されるわけですね。
なので、 void Func(int ai[3]); の 3 は書く必要は無くて、void Func(int ai[]); で事足りるわけです。
「関数に渡ってくるのは、結局はつねにポインタだ。」というのは、上記のような意味で正しいわけですね。

以上は、配列の値渡しの場合でしたが、C++ では配列の参照渡しもできて、この場合には関数のプロトタイプは void Func(int (&)[3]); のようになります。3 は省略できません。
関数定義では void Func(int (&ai)[3]) {} のようになります。
配列の参照渡しで関数に引数として渡るのは、元の配列の別名(上記例では ai)です。
良く理解してほしいのですが、配列の先頭アドレスとか、ポインタとかが渡るのではなく、元の配列をアクセスするための代理の名前が渡るという感じです。
例えば main() { int aiOriginal[3]; Func(aiOriginal); } のように配列を渡して、これを void Func(int (&aiAnotherName)[3]) { ... } で受けた場合、
aiAnotherName は aiOriginal の別名なので、aiAnotherName で配列の内容を弄ると、aiOriginal そのものを弄ることになります。
ここには、ポインタが云々という発想は全く無く、元の配列を別名で弄るということなのです。

投稿日時 - 2008-06-15 09:46:49

ANo.1

>関数の引数は式なので、配列は「先頭へのポインタ」に読み変えられる
これは仮引数の宣言の話ではなくて、

int func(int a[]);

なる関数を実際に呼び出す際に

int x[] = {1, 2, 3};
func(x);

でその引数 x が式だから先頭へのポインタと解釈される、ということじゃないのかな。

投稿日時 - 2008-06-15 09:39:57

あなたにオススメの質問