• 締切済み

関数のパラメータに配列を渡すときは、非参照型が普通なんですか?

 LippmanのC++プライマー(第4版)を勉強中です。 p.274以降に、配列に作用する関数の定義法と使用法に関する解説があり、p.275に以下の記述があります。 ■配列アーギュメント  配列パラメータも参照型と非参照型がある。  普通、配列は非参照型にする  非参照型パラメータは対応するアーギュメントのコピーで初期化される。配列アーギュメントはその配列の先頭要素へのポインタであり、そのポインタがパラメータにコピーされる。関数はアーギュメントのポインタを変更することはないが、パラメータのポインタを使って配列要素を変更することはできる。 ■配列を参照で渡すこと  配列パラメータを配列への参照にすることもできる。パラメータが配列への参照である場合、コンパイラは配列アーギュメントをポインタに変換しない。配列への参照そのものを渡す。この場合、配列の大きさはパラメータの型の一部である。コンパイラは配列アーギュメントの大きさがパラメータの大きさに一致するかどうかチェックする。 しかしp.268の「ヒント」には以下のように記されています。 ヒント:  Cの素養があるC++プログラマはアーギュメントにアクセスするためにポインタを渡すことに慣れている。 C++では、参照パラメータを使うのが安全かつ自然である。  配列パラメータも参照で渡したほうが、ポインタをコピーしないですむし、配列の大きさを越えてアクセスすることによる実行時エラーも抑止できるので、いいように思いますが、なんで「普通、配列は非参照型にする」んでしょうか?

みんなの回答

  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.6

ANo.1です。 私は全然分かっていませんでしたね。 失礼しました。 勉強になります。

mha01
質問者

補足

いえ、とんでもないです。 私もC++が分からないから質問しているわけでして(汗

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

完全に要素数が分からないときには参照にする意味はないです. あと, 要素数がわかっている場合であっても, 今時の普通の実装だと template <std::size_t N> void foo(int (&array)[N}); に対して int a1[5]; int a2[100]; を foo(a1); foo(a2); のように渡すと foo<5> と foo<100> の 2つの実体を作るわけだけど, それってどのくらいうれしいんでしょうか?

mha01
質問者

補足

回答ありがとうございます。 私は今時の普通の実装というものを全然知らないんですが、 int a1[5]; int a2[100]; を foo(a1); foo(a2); のように渡しても、全然うれしくないです。 ただ、 int a1[5]; double a2[100]; ................... ................... .... 中略.......... ................... ................... foo(a1); // foo(int(&)[5])と具現化 foo(a2); // foo(doubleI(&)[100])と具現化 となると、参照を使った方がポインタを使うよりも、多少はうれしいのかなと。 もっとも論点は、Lippmanが、 ポインタと配列はエラーの宝庫だから使うな、 配列を使いたくなったら、ベクトルを使え ポインタを使いたくなったら、参照を使え(もっともベクトルの時はイテレータを使うわけですけど) と強調していたりするのに、配列パラメータの話でいきなり「普通、配列は非参照型にする」とか言うものだから、「あれ?」みたいな感じになっているわけでして。

回答No.4

>なんで「普通、配列は非参照型にする」んでしょうか? 「過去のシガラミ」の為。 下手に仕様変更したら「CからC++に移植する時に、配列を(ポインタのつもりで)引数に渡している部分を、すべて書き替え」しなきゃならない。 >配列の大きさを越えてアクセスすることによる実行時エラーも抑止できるので、いいように思いますが それやられると「コンパイルできないソースコードが山のよう」に出て来るから駄目。 Windowsの某API関数では、ある構造体を typedef struct tagLOGPALETTE { WORD palVersion; WORD palNumEntries; PALETTEENTRY palPalEntry[1]; } LOGPALETTE; とかって定義してて「パレットデータは1個しかないよ」と言いつつ、実際には「パレットデータpalPalEntry[]の要素数はpalNumEntries個ある」などと言う事になってる。 こういう「実際には、定義よりも、配列の実際の要素数が多い」って使い方が良くあるので「参照渡しで要素数のチェックをされたら、移植が大変」になってしまう。 上記の例では「palNumEntriesがもし256なら、palPalEntry[0]~palPalEntry[255]までアクセス可」だったりする。たとえ「palPalEntry[1];」のように「配列要素は1個しかない」と定義されていたとしても。 あと、今は滅多に無いけど、昔のCのソースには「配列の添え字に、負の定数」などと言う、無茶な書き方をしている物もあったりするので、場合によっては「添え字チェックは大きなお世話」だったりします。

mha01
質問者

補足

結局、 「普通は、...」 って書いてあるのは、訳者の舌足らずで、 「通常見かける(昔の)コードでは、」 って解釈すればいいということでしょうか?

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.3

目的に応じた使い分けが大切です。 配列の要素数が事前に分からない場合には、引数を参照型として関数に渡すことができませんので、ポインタで渡す以外にはありません。 一方で、(例えば3次元座標を配列で表す場合のように)確実に要素数が3でなければならない場合には、参照渡しにした方が得策です。 要素数が可変で、かつ要素数の情報も一緒に渡したい場合には、要素数を表す引数を別途渡すか、下記のようにテンプレートを使うしかありません。 template <std::size_t N> void func(int (&array)[N]);

mha01
質問者

補足

>template <std::size_t N> >void func(int (&array)[N]); 結局、要素数が分からない場合は、値テンプレートパラメータを参照で使えば済むわけで、参照で統一したほうがいいのに、何で「普通は...」なんて書いてあるんだという辺りが何でかなぁと思っています。

noname#208124
noname#208124
回答No.2

膨大な配列を非参照型で渡しても配列のコピーなんて作られません どちらにせよあっというまのスタックオーバーフローなんて起きません 普通なんて知りませんが、自分は使い回しづらいと答えておきます

mha01
質問者

補足

 非参照の場合でも、配列のコピーは作られませんが、配列の先頭要素へのポインタのコピーが作られるので、無駄ではないかと思うんです。  また、参照で渡したほうが、配列の大きさ等の付加情報も関数側に知らされるので、配列の大きさに絡んだエラーも起きませんから安全だし、ポインタ絡みの事で注意を払う必要も無いので、良いんじゃないかと。  Stroustrupの下でC++の開発を行ってC++を知り尽くしている著者のLippmanが、C++ではポインタではなく参照を使うようにと推奨しているのに、配列パラメータの場合だけ「普通は非参照にする」と言っているので、何か明確な理由があるのか?、あったら知りたいと思ってます。

  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.1

参照型、非参照型、ですか。私は、値渡し、ポインタ 渡し、と習いました。 引数はスタックを使いますよね。 参照型も非参照型も配列でなければ、まあ同じような サイズを使います。ところが、配列とか文字列とか は膨大なスタックを消費することになります。 これで、再帰関数などにしたら、あっというまにスタッ クオーバになるでしょう。 そんなところに理由があるんじゃないでしょうか。

mha01
質問者

補足

えーと、値渡しとポインタ渡しというのは、C言語の世界の話でして、C++になると『参照』という、C言語にはない概念が出てきます。 なんでこんな概念を導入したかというと、ポインタ絡みのエラーを避けようという目的です。 >引数はスタックを使いますよね。 実引数(アーギュメント)で仮引数(パラメータ)を初期化するときに、実引数のコピーが生成されるのですが、そのコピーオブジェクトがスタックを使うので、膨大な配列とかを値渡ししたら、スタックオーバーになるのです。 参照はポインタの改良版みたいなもので、スタックオーバーにはならないと思うんですけど?

関連するQ&A