Cのポインタについて(関数への値渡し)

このQ&Aのポイント
  • C言語のポインタに関する質問です。関数に引数を渡す方法として以下の誤ったswap関数でなぜだめなのかいまいち得心できません。
  • 誤ったswap関数では、値をコピーしているため元の変数に影響を与えることができないため、交換ができません。
  • 正しいswap関数では、変数のアドレスを渡すことにより、直接変数の値を交換することができます。
回答を見る
  • ベストアンサー

Cのポインタについて(関数への値渡し)

C言語のポインタに関する質問です。関数に引数を渡す方法として以下の誤ったswap関数でなぜだめなのかいまいち得心できません。わかりやすくかみくだいて説明していただけると有り難いです。 できましたら、トレースともうしますか、変数の値の動きを詳細に段階的にプログラムの流れに沿って追っていって、だからこうなんだよ、みたいな解説がいただけたら有り難いです。わがままいってすみません。 /* 誤ったswap関数の宣言 */ void swap(int x, int y); int main(void) { int num1 = 5; int num2 = 10; printf("変数num1の値は%dです。¥n", num1); printf("変数num2の値は%dです。¥n", num2); printf("変数num1とnum2の値を交換します。¥n", num1); swap(num1, num2); printf("変数num1の値は%dです。¥n", num1); printf("変数num2の値は%dです。¥n", num2); return 0; } /* 誤ったswap関数の定義 */ void swap(int x, int y) { int tmp; tmp = x; x = y; y = tmp; } ---------- /* swap関数の宣言 */ void swap(int *pX, int *pY); int main(void) { int num1 = 5; int num2 = 10; printf("変数num1の値は%dです。¥n", num1); printf("変数num2の値は%dです。¥n", num2); printf("変数num1とnum2の値を交換します。¥n", num1); swap(&num1, &num2); printf("変数num1の値は%dです。¥n", num1); printf("変数num2の値は%dです。¥n", num2); return 0; } /* swap関数の定義 */ void swap(int *pX, int *pY) { int tmp; tmp = *pX; *pX = *pY; *pY = tmp; }

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

  • ベストアンサー
  • not_ace
  • ベストアンサー率52% (35/67)
回答No.3

ポインタはメモリ上のアドレスがイメージできていないと理解するのが難しいです。 /* 誤ったswap関数の宣言 */の例では渡されるのは「値」であってアドレスではありません。 上記例ではnum1に「5」、num2に「10」を入れた場合なので swap(num1, num2);によって関数には5、10が渡されますが、 関数が「void swap(int x, int y)」と定義されていると 引数であるint x、 int yは「swap(x,y)」が呼ばれたときだけ一時的に確保される変数になります。 なのでいくらswap関数内でx,yの値を操作しようとも、元のmain関数のnum1,num2には何の影響もありません。 対して正しいほうのswap関数では「void swap(int *pX, int *pY)」と定義することによって 変数の「アドレス」を渡しています。(このアドレスを持っているpX、pYをポインタと呼びます) *pXのようにポインタに「*」をつけるとpXが指し示すアドレスにある変数を変更することができます。 正しいswap関数の方ではswap(&num1, &num2);と書かれている(「&」をつけるとその変数のアドレスが渡される)ので、 pX、pYにはnum1、num2のアドレスが渡されます。 関数内では渡されたアドレスがさしている場所の変数の値を入れ替えているので正しくswapすることができます。 わかりやすい?例えで説明すると num1という名前の家とnum2という名前の家があります。 num1には「5」が、num2には「10」が住んでいます。 swap関数が呼ばれたときに「x」と「y」という仮設住宅が建てられて 「5」と「10」のコピー人間「5'」「10'」を住まわせます。 (xには「5'」、yには「10'」) 関数内の操作で「5'」と「10'」の住んでいる家を入れ替えます。 関数が終わると「5'」と「10'」が入れ替わって住んでいる仮設住宅は取り壊されますが 元の住宅には何も操作をしていないので num1には「5」が、num2には「10」が住んだままです。 以上が間違った例 次が正しい例 num1という名前の家とnum2という名前の家があります。 num1には「5」が、num2には「10」が住んでいます。 swap関数が呼ばれたときに「pX」と「pY」にはnum1とnum2の住所が書かれます 関数は住所を元にnum1とnum2の家を訪問し、住んでいる人を入れ替えます。 関数が終わるとnum1には「10」が、num2には「5」が入れ替わって住んでいます。 この説明がわかりやすいかいまいち自信がありませんが理解の助けになれば幸いです。

isymhdo
質問者

お礼

住宅と仮設住宅の例えがわかりやすく理解に役立ちました。有り難うございます。他の方も同様にアドバイスありがとうございました。この場でお礼申し上げます。

isymhdo
質問者

補足

わかりやすいご説明有り難うございます。 ちなみに誤ったSwap関数では void swap(intX, intY){***} が void swap(int Y, intX){***} に入れ替わっただけで値は5と10で無にも変わらない intX->num1(5) intY->num2(10) 理屈が飲み込めないんですよね(汗) そういうことでしょうか???

その他の回答 (6)

回答No.7

この説明ではどーですか? int num1 = 5;    →メモリ上の100番地が確保されてそこに5が入る int num2 = 10;    →メモリ上の104番地が確保されてそこに10が入る 誤ったswap関数の場合 swap(num1, num2);  →メモリ上の200番地と204番地が確保されて            それぞれに100番地と104番地の値5と10がコピーされる            この後、200番地と204番地の値が入れ替えられるが、            100番地と104番地の値は変わらない 正しいswap関数の場合 swap(&num1, &num2); →メモリ上の200番地と204番地が確保されて            それぞれに100番地と104番地というアドレスそのものがコピーされる            この後、200番地と204番地に入ってるアドレス(100番地と104番地)            の指し示す値が入れ替えられるので、num1とnum2の値が入れ替わる ※メモリの番地はあくまで例えです。  実際にはどこに確保されるのかは不定です。  メモリ上に確保されるとは限らない、とかの話もナシ。 >「変数のコピー」を引数にとる関数に「変数のアドレス値」を渡してしまっており、 > なおかつswapに渡された引数に対して関数内で直接代入してしまってます。 ポインタを使ったswap関数が間違っているかのような誤解を招く書き方ですが、 ポインタを引数にとる関数に「変数のアドレス値」を渡すのは当然のことです。 渡された引数に対して関数内で直接代入するためにポインタを使います。 ポインタを使ったswap関数は正しい使い方であり、何の問題もありません。

  • not_ace
  • ベストアンサー率52% (35/67)
回答No.6

ANo.3の回答者です。 すみません。 補足でおっしゃりたいことが良くわかりません。 どこがしっくりこないのかもうちょっと噛み砕いていただけますか?

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.5

> void swap(int &x, int &y) 『C言語には』参照渡しはありません。 参照渡しと同等のことをするなら、「ポインタを値渡し」することになります。 C++もやってると、ときどき混乱してしまいますが。 他にもstructが省略できないとか...

回答No.4

void swap(int x, int y) {  int tmp;  tmp = x;  x = y;  y = tmp; } と void swap(int *x, int *y) {  int tmp;  tmp = *x;  *x = *y;  *y = tmp; } の違いですが、まず明らかなのは引数のx、yの前に「*」が付いているかどうかですよね? これで引数の型が違うという風にコンパイラは解釈します。なにが違うのかというと関数に引数として与えた変数の、「変数が持つ値自体のコピー」か「変数のアドレス値」かという違いです。 変数のコピーとして引数を渡す場合は、その変数が実行される間だけ一時的に新しい領域が確保され、関数から抜けるときに開放されます。もちろん関数呼び出しの際に渡した引数の変数のアドレスとはことなるアドレスのものです。 変数に代入されている値と変数のアドレス値は違うものですよ。 あなたのプログラムだと、「変数のコピー」を引数にとる関数に「変数のアドレス値」を渡してしまっており、なおかつswapに渡された引数に対して関数内で直接代入してしまってます。こういう書き方をC言語で行うことも確かに過去にはありましたがこういう書き方はしないでください。 もしそういう風に書きたいのであれば「参照引数」を使ってください。以下のように書きます。 void swap(int &x, int &y) {  int tmp;  tmp = x;  x = y;  y = tmp; } 参照引数値は引数として渡した変数と「同じ値かつ同じアドレス値」を持つものです。このように書けば誤りではなくなりますし、処理も正常に行われるでしょう。 高級言語では意識することが少なくなっているのかもしれませんが、プログラム内で使用する全ての変数にはメモリのどこかにその領域が割り当てられて、その場所を示している値が「ポインタ値」でその「ポインタ値から始まる変数のサイズ分の領域(バイト単位)」に書き込まれたデータです。 特にC/C++ではポインタを使った処理がありますので、「ポインタ値」と「変数値」の違いを認識されることから始められてはいかがでしょうか。 http://www7b.biglobe.ne.jp/robe/cpphtml/

  • tadys
  • ベストアンサー率40% (856/2135)
回答No.2

C言語の関数で渡す引数は全て元の変数のコピーです。 そのコピーは関数から戻る時にすべて廃棄されます。 コピーの値をいくらいじったところで元の変数の値が変化する事はありません。 元の変数の値を変化させようとするのであれば、その変数の保存場所(変数のアドレス)を関数に渡す必要が有ります。 戻り値として戻す方法もあります。

  • jjon-com
  • ベストアンサー率61% (1599/2592)
回答No.1

/* 誤ったswap関数 */ int num1 = 5; …変数num1を確保する int num2 = 10; …変数num2を確保する swap(num1, num2); …num1とnum2の中身を別関数に送る  ↓  ↓  ↓ void swap(int x, int y)   …変数xと変数yを確保し,受け取った値を代入する。 値渡し(値をコピーして渡す)ですから, xの中身は5ですし,yの中身は10ですが,num1やnum2とは別の領域です。 xやyの中身をどう変更しても,num1やnum2の中身は変化しません。

関連するQ&A

  • 誤った関数に関する値渡しについて

    こんにちは、まずはソースを記述します。 #include<iostream> using namespace std; //誤ったswap関数の宣言 void swap(int x, int y); int main() { int num1 = 5; int num2 = 10; cout << "変数num1の値は" << num1 << "です。\n"; cout << "変数num2の値は" << num2 << "です。\n"; cout << "変数num1とnum2の値を交換します。\n"; swap(num1, num2); cout << "変数num1の値は" << num1 << "です。\n"; cout << "変数num2の値は" << num2 << "です。\n"; return 0; } //誤ったswap関数の定義 void swap(int x, int y) { int tmp; tmp = x; x = y; y = tmp; } >>関数内で仮引数xとyの値を交換する処理を行っていても、これは変数num1とnum2の値を「コピー」した5と10を交換しているにすぎません。 swap関数内で値を交換しても、呼び出し元の変数であるnum1とnum2に影響を与えることができません。 ・・の文章の中から質問ですが値渡しとはどういうことでしょうか? 参照渡しとはどう違うのでしょうか? ご教示お願いします。

  • ポインタが全く分かりません。

    今C言語を勉強していて、先日やっとポインタに取り組み始めました。でもいきなりわけ分からなくなってしまいました。自分なりに本やホームページで調べてみたのですが、説明が全く載っていなかったので、質問させてもらいました。 /* 2つの値の交換 (正) */ #include<stdio.h> void swap(int *px, int *py) { int tmp; tmp = *px; *px = *py; *py = tmp; } void main() { int a=3, b=5; printf("a=%d, b=%d\n", a, b); swap(&a, &b); printf("a=%d, b=%d\n", a, b); return 0; } 実行結果  a=3, b=5         a=5, b=3 /* 2つの値の交換 (誤) */ #include<stdio.h> void swap(int *px, int *py) { int tmp; *px = tmp; *px = *py; *py = tmp; } void main() { int a=3, b=5; printf("a=%d, b=%d\n", a, b); swap(&a, &b); printf("a=%d, b=%d\n", a, b); return 0; } エラーメッセージ 『Warning:'tmp' used before set』 実行結果  a=3, b=5         a=5, b=4404 tmp = *px と *px = tmp は同じに見えるのですが、なぜ tmp = *px だとちゃんとできて、*px = tmp とした時はエラーが出るのでしょうか。 分かる人には申し訳ないほどの初歩的な質問でしょうが、全く分からないのでめちゃくちゃ困ってます。初心者にも分かりやすいように、なるべく詳しく回答していただけると、とても嬉しいです。

  • C++ 関数プロトタイプと値渡し・参照渡しについて

    次のコードは、入門書にあった値渡しのサンプルです。 値渡しなので、a=5 ,b=10が出力されます。 void swap(int x,int y); //←抜けていた int main(){ int a=5; int b=10; swap(a,b); cout<<"a="<< a << "\n"; cout<<"b="<<b<<"\n"; } void swap(int x,int y){ int tmp=x; x=y; y=tmp; } しかし、自分で入力したところ何故かa=10,b=5が出力されました。 (VisualC++2008で実行しました。) よく見てみると、上記1行目の関数プロトタイプが抜けていました。 入門書を読んだ限りでは、次の2点が理解できません。 (1)main関数の後ろにswap関数があり、関数プロトタイプが無いのでコンパイルエラーになるはずなのにならない (2)値渡しのはずなのに参照渡しと同じ結果になる よろしくお願いします。

  • C言語のポインタの考え方について

    ポインタについて理解ができていないのでお聞きしたいのですが 値を交換する関数のプログラミングでこの場合ポインタ で以下にしないといけないと思います。 #include<stdio.h> void swap(int *a int *b){ int c; c=*a; *a=*b; *b=c; } main(){ int x,y; x=123; y=456; swap(&x,&y); printf("x = %d, y = %d\n", x, y); } またポインタを使用せず以下のプログラムではなぜダメのでしょうか。 よろしくお願い致します。 #include<stdio.h> void swap(int a int b){ int c; c=a; a=b; b=c; } main(){ int x,y; x=123; y=456; swap(x,y); printf("x = %d, y = %d\n", x, y); }

  • ポインタを使ったソートプログラム

    #include<stdio.h> void swap(double *a,double *b) { double tmp; tmp=*a; *a=*b; *b=tmp; } void sort3d(double *pa,double *pb,double *pc) { if(*pa>*pb) { swap(pa,pb); } if(*pb>*pc) { swap(pb,pc); } if(*pa>*pc) { swap(pa,pc); } } int main(void) { double num1=3.14; double num2=2.97; double num3=0.01; sort3d(&num1,&num2,&num3); printf("d1の値=%.3d\n",num1); printf("d2の値=%.3d\n",num2); printf("d3の値=%.3d\n",num3); return 0; } ポインタを使ったソートプログラムを作ってみました。 ところが、コマンドプロンプトを使って動作させたら、 結果がうまく表示されませんでした。 どこがおかしいのか指摘していただけると嬉しいです。

  • c言語 select sort

    最大値検索法のプログラムコードです。 どこがおかしいのでしょうか? 分かる方、教えてください。 よろしくおねがいします。 swapのプログラムコード #include <stdio.h> void swap(int *px,int *py); int main (void) { FILE *fp; if ((fp=fopen("file.txt","rt"))==NULL){ printf("File open error.\n"); return 0; } int i,a[100]; for(i=0;i<100;i++){ fscanf(fp,"%d,",&a[i]); //ファイルから読み込み処理。// } fclose(fp); for(i=0;i<10;i++) printf("[%d]=%d\n",i,a[i]); /*1.ソートすべきデータの中で最大のデータを見つけ、 2.そのデータを最後のデータと入れ替える。 最大データは配列のどこにあるのか⇒maxi              その値⇒max とする。*/ //データが10個の場合 int max,maxi,j; max=a[0],maxi=0; for(i = 0;i < 9; i++){ if(a[i + 1] > max){ max = a[i + 1]; maxi = i + 1; } swap(&a[maxi],&a[9-j]); /* コマンド $cc sort.c swap.c */ for(j=0;j<9;j++){ printf("%d\n",j); max=a[0], maxi=0; for(i=0;i<9-j;i++){ //最大値をもつデータ探索;(カウンタ変数) max++; } //最大データと探索範囲最後のデータとの入れ替え: //void swap(int *px, int *py){ int n,*px,*py; n = *px; *px = *py; *py = n; // } if((fp=fopen("file.txt","wt"))==NULL){ printf("File open error.\n"); return 0; } for(i=0;i<100;i++){ fprintf(fp,"%d",a[i]); } fclose(fp); } } sort.cのプログラムコード #include<stdio.h> void swap (int *px,int *py); int main(void) { int a[0],b,maxi,j,max; max=a[0],maxi=0; printf("input \"a\" as integer = "); scanf("%d",&a); printf("input \"b\" as integer = "); scanf("%d",&b); printf("Before swap...\n"); printf("a - b = %d, a / b = %d...%d\n",a-b,a-b,a-b); // swap(&px,&py); swap(&a[maxi],&a[9-j]); printf("After swap...\n"); printf("a - b = %d, a / b = %d...%d\n",a-b,a-b,a-b); return 0; } void swap (int *px,int *py) { int n; n = *px; *px = *py; *py = n; } 実行結果 /tmp/ccBGIpCi.o(.text+0x0): In function `main': : multiple definition of `main' /tmp/ccMCttJd.o(.text+0x0): first defined here /usr/bin/ld: Warning: size of symbol `main' changed from 304 in /tmp/ccMCttJd.o to 641 in /tmp/ccBGIpCi.o collect2: ld はステータス 1 で終了

  • ポインタ渡しについて

    関数の引数をint型のポインタで出力し、その関数を呼び出し側で値を取得するにはどうしたらいいのでしょうか? 一応ソースを作成しました(下記参照)が、ほしい値が出てきません。 よろしくお願い致します。 ----------------------------------------------- #include <stdio.h> int check(int *num){ int n=10; num = &n; return 0; } int main() { int m_num; check(&m_num); printf("%d\n",m_num); return 0; } ----------------------------------------- m_numに、10が格納されません。 check()でアドレス渡しをしたのですが・・・。 どうすれば、main関数で、数値を扱うことが出来ますか? よろしくお願い致します。

  • 関数についての質問です。

    c言語を勉強しています。関数で戻り値がありますが、関数の処理の仕組みが理解できません。本等で勉強し僕の考え方が間違っていたら教えてください。 お願いいたします。 #include <stdio.h> int buy(int x,int y ) { int z; //1 printf("%d万円と%d万円の車を買いました。");//2 z=x+y; //3 return z; //4 } int main(void) { int num1,num2,num;//5 printf("いくらの車をかいますか?\n");//6 scanf("%d",&num1);//7 printf("いくらの車をかいますか?\n");//8 scanf("%d",&num2);//9 sum=buy(num1,num2);//10 printf("合計で%d万円です。\n",sum);//11 return 0;//12 } まず最初に 整数型、int num1,num2,numを読みます。 次に//7と//8で数値を入力します。 そして//10で値を格納し int buy( int x,int y)に値をわたします。 ※正確には関数buyに渡す。 そして計算をし計算結果 zをreturn で int buy(int x,int y )の buyに値を渡します。 そのあと呼び出し元の//10のbuyに値をかえします。 そしてプログラムが終了します。 間違っていたら教えてください。

  • C 関数とポインタ

    ポインタと関数がよく分かりません。 (日本語がおかしくてすみません(^_^;)) たとえば↓のようなプログラムで、 #include <stdio.h> void increase(int *i); int main(void) { int x = 3; increase(&x); printf("%d\n", x); return 0; } void increase(int *i) { (*i)++; } 結果は4になりますが、increase(&x)が&xとなっていて、 関数はvoid increase(int *i)でint *iになっているのですが、 これはvoid increase(int *i)はint型の「ポインタ」なので、 increase(&x)も&xと「アドレス」を渡さなければいけないということですか?? そして、void increase(int *i)内では、アドレス&xの指す値をインクリメント、という考えで良いのでしょうか?

  • 二つ以上の値を返す関数

    たとえば、a,bという値を関数に入れて、関数の中で変わったa,bを受け取るような方法はないでしょうか。 http://oshiete1.goo.ne.jp/kotaeru.php3?q=138798 の int main(void) { int a=1, b=2; func(&a, &b); printf("%d, %d\n", a, b); return 0; } void func(int *x, int *y) { *x++; *y++; } を実行してみましたが、 「関数 'func' は定義されていません。int 型の値を返す外部関数と見なします。」 と出てきました。 助言をよろしくお願いします。