C言語基礎|引数の値渡し

関数に渡るのは“実物”じゃなくて“コピー”だよ

関数を呼び出すとき、私たちは実引数を渡しますよね。
でもC言語で基本になる考え方はこれです。

関数に渡るのは、値(データそのもの)のコピー。
だから、関数の中で仮引数をいじっても、呼び出し元の変数は基本的に変わりません。

この仕組みを 値渡し(pass by value) と呼びます。
最初は「え、じゃあ関数の中で書き換えても意味なくない?」って思うかもしれませんが、実はこれが“安全”で“分かりやすい”設計なんです。

サンプルプログラム

題材は 税込み金額を計算する関数。そして値渡しのポイントが見えるように、関数内で仮引数を変更してみます。

  • 関数 calc_taxed:税率を使って税込み金額を返す。
  • 関数内で price を加工しても、main 側の price は変化しないことを確認

プロジェクト名:chap6-6-1 ソースファイル名:chap6-6-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

// 税込み金額を返す(値渡しの確認つき)
int calc_taxed(int price, int rate)
{
    // わざと仮引数priceを変更してみる(値渡しの確認用)
    price = price + 100;  // 「送料100円追加」と仮定

    // 税込み計算(例:rate=10なら10%)
    return price + price * rate / 100;
}

int main(void)
{
    int price, rate;

    puts("税込み金額を計算します。");
    printf("本体価格(円):");
    scanf("%d", &price);
    printf("税率(%):");
    scanf("%d", &rate);

    printf("計算結果:%d円です。\n", calc_taxed(price, rate));
    printf("確認:入力した本体価格は %d円 のままです。\n", price);

    return 0;
}

実行例

税込み金額を計算します。
本体価格(円):1000
税率(%):10
計算結果:1210円です。
確認:入力した本体価格は 1000円 のままです。

関数の中では price に 100 を足していますが、main の price は変わっていません。
これが値渡しの感覚です。

値渡しのイメージ

値渡しは「実引数の値が、仮引数に代入される」と考えるとスッキリします。

値渡し(コピー)の流れ

呼び出し側(main)呼び出し呼び出され側(calc_taxed)
price = 1000calc_taxed(price, rate)price は 1000 のコピー
rate = 10rate は 10 のコピー

そして関数内で price を 1100 にしても、それは 関数内のコピー を書き換えただけ、というわけです。

なぜ値渡しだと安心なの?

値渡しのメリット

ポイントどう安心?
呼び出し元が勝手に壊れない関数の中で仮引数を変更しても元データはそのまま
関数の影響範囲が小さい“戻り値”だけ見れば動作が追える
デバッグしやすい引数がコピーなので状態が予測しやすい

登場する命令・書式と役割(しっかり整理)

関数定義の書式

  • 返却値型 関数名(仮引数の宣言…) { 処理 }

例:

  • int calc_taxed(int price, int rate) { ... }

関数呼出しの書式

  • 関数名(実引数, 実引数, ...)

例:

  • calc_taxed(price, rate)

return文の書式

  • return 式;
  • return;(返却値型が void のとき)

今回の命令・演算子

名前書式何をする?
scanfscanf(書式, 変数のアドレス...)キーボード入力を変数に格納する。
printfprintf(書式, 値...)画面に表示する。
putsputs(文字列)文字列を表示して改行する。
returnreturn 式;関数を終了し、値を呼び出し元へ返す。
代入変数 = 式変数の値を更新する。
四則演算+ - * /計算する(整数同士の割り算は小数が切り捨て)。

関数内で仮引数を変更しても、なぜ実引数は変わらない?

ここがこの記事のキモです。

  • 実引数の値が仮引数にコピーされる。
  • 関数の中で変更されるのは「仮引数(コピー)」だけ。
  • 呼び出し元の変数は別物なので影響しない。

本のコピーにメモしても、元の本は汚れない、という例えがまさにそれですね。

型が違う引数を渡すとどうなる?(暗黙の型変換)

例えば今回の関数は int を受け取るので、次のような呼び出しは基本OKです。

  • calc_taxed(1000, 10)

もし double や float の値を渡そうとすると、Cでは状況に応じて 暗黙の型変換 が起きます。
ただし「文字列を数値に変換」みたいな都合のいい変換はできないので、そういう場合はエラーになります。

暗黙の型変換のざっくり感覚

渡す値受け取る型起きること
5double5.0 に変換されやすい(格上げ)
2.9int2 になりやすい(小数切り捨て)
"abc"int や double数値にできないのでエラー

演習問題

演習6-5:1からnまでの合計を返す関数を作ろう

次の関数を作成せよ。

  • int sumup(int n);

条件:

  • 1 から n までの全整数の和を返す。
  • main 関数で動作確認も行う。

解答例

プロジェクト名:chap6-6-2 ソースファイル名:chap6-6-2.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int sumup(int n)
{
    int sum = 0;
    for (int i = 1; i <= n; i++)
        sum += i;
    return sum;
}

int main(void)
{
    int n;

    puts("1からnまでの合計を求めます。");
    printf("n:");
    scanf("%d", &n);

    printf("合計は %d です。\n", sumup(n));
    printf("確認:nは %d のままです。\n", n);

    return 0;
}

解説

  • n は値渡しなので、sumup の中で i を回しても main の n は変わりません。
  • “入力n → 返却値sum” という関係がシンプルで追いやすいです。