Chomado's Blog
You Are Reading

C言語における値渡しと参照渡し

25
C言語, プログラミング

C言語における値渡しと参照渡し


[追記]
この記事は2014年、私が文系大学生の頃、手探りでプログラミングを独学し始めた頃の記事です。温かい気持ちで見ていただけたら幸いです。

ーー

ポインタ

ごっちゃになってきたので自分用メモに書いたものを載せます.

#include <stdio.h>
int main()
{
    int a;
    a = 10;

    int *pa;
    pa = &a;

    /** アドレスを表示 **/
    printf("%p\n", &a);

    /** 変数aに格納された値を表示 **/
    printf("%d\n", *pa);

    return 0;
}
% ./pointer
0x7fff5971e808
10

値渡しと参照渡し

値渡し #とは

ググりました!(((o(゚▽゚)o)))

値渡し 【 call by value 】 値呼び出し.
プログラム中で関数やサブルーチンなどに引数を渡す時に、その値のみを渡す方式。「値呼び出し」とも呼ばれる。渡された関数などの中で値を変更しても、呼び出し元の変数の内容は変わらない。
これに対し、変数への参照(メモリ中のアドレスなど)を渡し、関数などの中で値を変更すると元の変数も同じように変更される方式を「参照渡し」(call by reference)という。

<引用: http://e-words.jp/w/E580A4E6B8A1E38197.html >
φ(゚▽゚)oメモメモ

参照渡し #とは

参照渡し 【 call by reference 】 参照呼び出し.
プログラム中で関数やサブルーチンなどに引数を渡す時に、変数への参照(メモリ中のアドレスなど)を渡す方式。「参照呼び出し」とも呼ばれる。関数などの中で値を変更すると元の変数も同じように変更される。
これに対し、変数の値のみを渡し、渡された関数などの中で値を変更しても呼び出し元の変数の内容が変わらない方式を「値渡し」(call by value)という。

<引用: http://e-words.jp/w/E58F82E785A7E6B8A1E38197.html>

実際に動かす

dotinstall #21 参照渡しをつかってみよう (1) より.
http://dotinstall.com/lessons/basic_c/9421
( dotinstallは動画で学べてとてもわかり易いです!(((o(゚▽゚)o)))私は去年からお世話になっています! )

#include <stdio.h>

/*** 値渡し (call by value)***/
/* main関数の元々のaの値は保持される */
void f(long a) 
{
    a += 100;
    printf("%ld\n", a);
}

/*** 参照渡し (call by reference)***/
/* main関数の元々のaの値が書き換えられる */
void f2(long *pa) 
{
    *pa += 100;
    printf("%ld\n", *pa);
}

int main(void)
{
    long a = 1000;
    f(a);
    f2(&a);

    return 0;
}

実行結果

% ./dotinstall-c-21
1100
1100

C言語の場合, ポインタをうまく使えば参照渡しができるみたいですね!

メモリ上ではこうなっています.
スクリーンショット 2014-02-27 17.45.21
(画像はドットインストールの講座から持ってきました http://dotinstall.com/lessons/basic_c/9421 )

左が値渡し, 右が参照渡しです. 右のほうがちょっとメモリを節約できています.

でも, 参照渡し, 元々の値が書き換えられるって怖いです..

しかし, 参照渡しでしかできないことがあるみたいです!(((o(゚▽゚)o)))
よく参考書で出てくる swap 関数です! aとbの2つの引数を受け取り,それらの中の値を入れ替えます.

参照渡しを利用し, swap関数を作る

dotinstall #22 参照渡しをつかってみよう (2)
http://dotinstall.com/lessons/basic_c/9422

#include <stdio.h>

void swap(int *pa, int *pb)
{
    int tmp;
    tmp = *pa;
    *pa = *pb;
    *pb = tmp;
}

int main (void)
{
    int a = 5;
    int b = 10;
    printf("**** 入れ替える前******\n");
    printf("a: %d, b: %d\n", a, b);

    swap(&a, &b);

    printf("**** 入れ替えた後******\n");
    printf("a: %d, b: %d\n", a, b);

    return 0;
}

実行結果

% ./swap
**** 入れ替える前******
a: 5, b: 10
**** 入れ替えた後******
a: 10, b: 5

参照元(dotinstall)

dotinstall

感想

個人的に思ったものです.

参照渡しは, 元々の値が書き換えられちゃうから, 値渡しのほうが安全な印象を持ちました.
イメージとしては, ドラクエ5で, サラボナの街の結婚相手を選ぶ前夜に, フローラとビアンカ(とデボラ)の誰を選ぶかルドマンに告げる直前に教会でセーブデータを3つ作っておく感じです.

でも, 値渡ししまくってると, 関数を呼び出すたびに、引数に渡したデータがコピーされ、スタックに積まれてしまうので, 大きなプログラムでたくさんの関数が動いてる時に (returnしてパラメータの分のメモリが開放されるまでは) オーバーフローしないかちょっと心配? とも思いました.

私はまだあんまりコード書いてない(経験不足だ)から, 知識が地面につかずゆるふわな理解だけど, とりあえず双方はトレードオフな関係なのかなって思いました.(小並感


Madoka Chomado (ちょまど)

千代田まどかです。よく「ちょまど」と呼ばれます。Microsoft 社員。文系出身プログラマ兼マンガ家です。

(4) Comments

  1. cointoss1973 says:

    値渡し、安全そうですね。

    値渡しにすればコピー先が仮に壊れても安全、そう信じていた人が書いたソースをメンテナンスしていました。
    関数呼び出しのたびに大量の値(おもに構造体)を引数につむので、スタックが大量に消費されます。あるタスクが消費するスタックの最大量は、実行時にしかわからない。特に組み込み向けOSですと、スタックオーバーフローしても検知できずそのままメモリ破壊して暴走、実行時におかしな例外が発生することもありました。
    ぜんぜん安全じゃないですね。

    そこで引数を int *p から const int *p とかでconst をつけてあげると 値の書き換えは不可能になります。 const int *p const ってやると値の書き換えはもちろん、アドレスの変更も不可能になります(コンパイラエラーになる)

    適切に const をつけることで可読性と安全性があがりますね。

  2. 匿名 says:

    C も Javaも,引数はすべて値渡しであって,参照渡しはない
    ここで説明されているのは,ポインタの値渡しであるにすぎない(実装上は,実引数=ポインタ変数の値が仮引数にコピーされるだけ)

  3. 通りすがり says:

    匿名の方の言われるとおり、Cには参照渡しはない
    C++だったら、本物の参照渡しがあるの
    void swap(int&a,int&b){int t=a;a=b;b=t;}
    int main(void){ swap(a,b); }

匿名 へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です