- 締切済み
引数のポインタについて
関数で、アウトプット引数をポインタにするのはわかるのですが、 インプット引数をポインタにする理由って何かあるのでしょうか?
- みんなの回答 (10)
- 専門家の回答
みんなの回答
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
#include <stdio.h> void zero(int* n) { *n = 0; } int main() { int n = 123; zero(&n); printf("n = %d\n", n); /* n = 0 */ return 0; } ポインタ無しにコレは書けない。
- akayoroshi
- ベストアンサー率50% (46/91)
#7の回答を載せたものです。 > 長さに制限を付ければ可能ですが、長さ制限なしで実現できますか? 後出しで悪いと思っていますが、投稿してすぐにこのことに気づきました。 「文字列の長さに上限を設けてよいのであれば」という制限を付けるべきでした。
- wormhole
- ベストアンサー率28% (1626/5665)
#7の方へ >も、配列をメンバーとする構造体を作り、関数のパラメータだけでなく戻り値の型もその構造体にして、関数を引用するプログラム内の代入式で構造体オブジェクトを更新するという手法を使えば、さほどの労力を費やさなくても実現できます。 長さに制限を付ければ可能ですが、長さ制限なしで実現できますか? strcpy(),strcat(),strcmp(),memcpy()のいづれも長さの制限ありませんが。 C99で構造体の最後の要素だけ可変長配列を扱えるようにはなりましたけど引数として処理される場合は長さ0扱いですよ?
- akayoroshi
- ベストアンサー率50% (46/91)
C言語では、パラメータの渡し方は、値渡ししかありません。関数を引用するときは仮引数の型と同じ型の任意の式を実引数に指定することができます。ポインタ型の仮引数の時は任意のアドレス式を実引数に指定できます。実引数の値が仮引数の初期値になります。引数が構造体であれば、実引数の構造体オブジェクトのすべてのメンバーの記憶領域が仮引数の記憶場所にコピーされます。引数がポインタ型であれば実引数の値(アドレス値)が仮引数にコピーされてそれが仮引数の初期値になります。 ということで、#1の回答が的を射ていると思います。 配列名を式の中で使うと(例外はありますが)、配列名は先頭の配列要素へのポインタになります。配列名を実引数とするときは仮引数をポインタ型変数にします。(仮引数を配列として宣言しても関数内ではアドレス値を保存するためのポインタ変数のための記憶場所だけが確保されます。C言語の仕様としてそのように決められています。)したがって関数の中では要素の値の参照しかなされない配列であっても、仮引数はポインタ型の変数になります。 C++言語では値渡しに加えて参照渡しが追加されました。これは、仮引数の変数のための記憶場所を関数内に確保するのではなく、実引数の記憶場所を仮引数で宣言した名前で参照するという機能です。これにより実引数から仮引数への値のコピーは不要になるので、構造体オブジェクトを渡すときなどに使われています。 #4の回答で、参照渡しと「ポインター渡し」を対照していますが、そもそも「ポインター渡し」などという用語はありません。ポインタ型の参照変数を使うことはできます。 > 参照渡しでは、NULLポインターを渡せません。 と書かれていますが、これは誤っています。#4の回答の中で示された例には #include <iostream> #include <cstring> void testNULL( char *const &s ) { if ( s==0 ) std::cout << "NULL pointer\n"; else if ( std::strlen(s) == 0 ) std::cout << "Zero length\n"; else std::cout << s << "\n"; } int main(void) { testNULL(NULL); testNULL(""); testNULL("OK!"); } で反証できます。(もっとも、この例では仮引数を参照変数にする必要はありません。) また、配列全体を値渡ししたいときは、配列をメンバーとする構造体を作ってそれを引数にすることで実現できます。構造体オブジェクトを引数にすると、メンバーが配列であっても、すべてのメンバーの記憶場所の値が実引数から仮引数にコピーされます。仮引数の構造体メンバーになっている配列の要素の値を変更しても実引数のメンバーの記憶場所が書き換えられることはありません。 #6の回答で示された > たとえばstrcpy()やstrcat()、strcmp()、memcpy()などと同等の動作をする関数を作ってみましょう。 > # もちろんポインタは使用禁止で。 も、配列をメンバーとする構造体を作り、関数のパラメータだけでなく戻り値の型もその構造体にして、関数を引用するプログラム内の代入式で構造体オブジェクトを更新するという手法を使えば、さほどの労力を費やさなくても実現できます。
- Wr5
- ベストアンサー率53% (2173/4061)
では……関数の引数でポインタを使わないように設計&実装してみればよいかと。 文字列を渡すことはできなくなりますが、そんなのはグローバル変数を多用すれば無問題です。 作った関数のウチ1つを別プロジェクトでも使いたい。 というときにそのグローバル変数も用意する必要がありますし、他で使ってるグローバル変数と名前が衝突するかもしれませんが、 文字列を渡すのにポインタを使うよりソースコード中の変数名を修正すればいいのですから無問題です。 もれなく、食い違いなく変更する必要がありますがそんなミスはアリエナイでしょう。 たとえばstrcpy()やstrcat()、strcmp()、memcpy()などと同等の動作をする関数を作ってみましょう。 # もちろんポインタは使用禁止で。 『意地でも引数にポインタを使用しない。』 というのを徹底できるか試してみましょう。 メンバ変数が大量にある構造体を引数で渡してみたりとか。 ポインタなら数バイトのローカル変数領域の消費。構造体自体を渡すとなったら構造体サイズ分のローカル変数領域の消費とコピーが必要になります。 が、ポインタなんていう意味不明なもののやりとりよりは健全で理解しやすいでしょう? # 効率?メモリ容量? # 今時のパソコンなんて速いしメモリもたっぷりなんですからそんなの考えるのは無駄無駄無駄ァ!! ま、そんなプログラミングだと組み込み系とかじゃやってられないでしょうけどね。
- wormhole
- ベストアンサー率28% (1626/5665)
「レジスタ」とかの話が出てるのは真に受けないように。 引数の値がレジスタ渡しかどうかは処理系によりますし引数にポインタを使うのとは全くの無関係の話です。
- hanabutako
- ベストアンサー率54% (492/895)
C・C++についての質問なのでポインター渡しと値渡しの違いについて聞いているのか、C++での参照渡しとポインター渡しの使い分けについて聞いているのか、どっちなのか今ひとつわかりませんでした。 ポインター渡しと値渡しの違いについては既に先に回答がある通りです。おおまかに言って次の2つでしょう。 1. 容量が大きい物を手早く渡したい 2. ポインター型のものを渡したい 1について、 構造体を関数の引数とする場合、構造体のなか見すべてをコピーするので例えば1024バイトの構造体を値渡しすると、1024バイトの値をそこでコピーします。関数の呼び出しごとにコピーが起きて、無駄にCPUを消費します。 既に他の方が書いているとおり、これがポインター渡しならアドレスを表すのに十分な容量 (sizeof(void*)の大きさ)をコピーするだけです。多くの場合はこちらのほうが容量が少ないのではないでしょうか。 また、関数の引数が大量にある場合や関数の引数が後で増える可能性がある場合、関数の引数に全てをだらだら書くのではなく、一旦構造体に入れて渡すということもされます。 2について、 C言語やC++で配列というのは事実上、配列の先頭番地へのポインター変数です。よって、配列を渡す場合は必然的にポインター渡しとなります。 C++での参照渡しとポインター渡しの使い分けについては、次の2つでは無いでしょうか。 a. NULLポインターを使いたい場合 b. C言語と共用する関数である場合 aについて、 参照渡しでは、NULLポインターを渡せません。例えば、空文字列と無効値を区別したい場合、""とNULLとするのが簡単だと思いますが、これを参照渡しで渡すことはできません。 この場合、NULLポインターを渡せるようにするためにポインター渡しにする必要があるでしょう。 bについて C言語には参照渡しという概念がありません。C言語と共用する関数のインタフェースやC言語から呼び出す窓口となる関数には参照渡しではなく、ポインター渡しを使う必要があるでしょう。 1、2、a、b いずれの場合でも、インプット引数が関数内で破壊されないことを保証するために、可能ならばconst修飾子をつけたほうがよいでしょうね。
- Tasuke22
- ベストアンサー率33% (1799/5383)
引数の受け渡しはレジスタで行われます。 レジスタに収まらないものはポインタで渡すしか無いわけです。 アウトプットもレジスタを書き換えたとしても、復帰した時にレジスタが元に戻されるので、呼び出し側に引き渡すことが不可能なため、メモリ上に書き込む必要があるわけです。 実際にはちと違うけど、説明の都合上単純化ということで。 今は64bitが主流なので、文字列も8文字も入るわけですが、8文字までの文字列ならレジスタで渡せるか、という訳にはいきません。 文法がややこしくなるというか、マシン依存、OS依存が激しくなるので統一という意味でも、文字数や配列数での例外が無いわけです。 プログラムで属性を変えて受け渡しは出来ますが、この場合もアプリがマシン依存、OS依存が発生し好ましくありません。 コードが長くなるのも面白くありません。 因みに、私が作ったWindows95のゲームがWindows7 64bitでもバイナリのままで動いていますよ。 依存性の乏しいコードにすると寿命が長くなります。
- kmee
- ベストアンサー率55% (1857/3366)
「文字列」や配列は、ポインタで渡すしかありません。
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
引き渡したいモノがどんなに大きくても、そのポインタなら数バイト。