
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 = 1000 | calc_taxed(price, rate) | price は 1000 のコピー |
| rate = 10 | rate は 10 のコピー |
そして関数内で price を 1100 にしても、それは 関数内のコピー を書き換えただけ、というわけです。
なぜ値渡しだと安心なの?
値渡しのメリット
| ポイント | どう安心? |
|---|---|
| 呼び出し元が勝手に壊れない | 関数の中で仮引数を変更しても元データはそのまま |
| 関数の影響範囲が小さい | “戻り値”だけ見れば動作が追える |
| デバッグしやすい | 引数がコピーなので状態が予測しやすい |
登場する命令・書式と役割(しっかり整理)
関数定義の書式
- 返却値型 関数名(仮引数の宣言…) { 処理 }
例:
- int calc_taxed(int price, int rate) { ... }
関数呼出しの書式
- 関数名(実引数, 実引数, ...)
例:
- calc_taxed(price, rate)
return文の書式
- return 式;
- return;(返却値型が void のとき)
今回の命令・演算子
| 名前 | 書式 | 何をする? |
|---|---|---|
| scanf | scanf(書式, 変数のアドレス...) | キーボード入力を変数に格納する。 |
| printf | printf(書式, 値...) | 画面に表示する。 |
| puts | puts(文字列) | 文字列を表示して改行する。 |
| return | return 式; | 関数を終了し、値を呼び出し元へ返す。 |
| 代入 | 変数 = 式 | 変数の値を更新する。 |
| 四則演算 | + - * / | 計算する(整数同士の割り算は小数が切り捨て)。 |
関数内で仮引数を変更しても、なぜ実引数は変わらない?
ここがこの記事のキモです。
- 実引数の値が仮引数にコピーされる。
- 関数の中で変更されるのは「仮引数(コピー)」だけ。
- 呼び出し元の変数は別物なので影響しない。
本のコピーにメモしても、元の本は汚れない、という例えがまさにそれですね。
型が違う引数を渡すとどうなる?(暗黙の型変換)
例えば今回の関数は int を受け取るので、次のような呼び出しは基本OKです。
- calc_taxed(1000, 10)
もし double や float の値を渡そうとすると、Cでは状況に応じて 暗黙の型変換 が起きます。
ただし「文字列を数値に変換」みたいな都合のいい変換はできないので、そういう場合はエラーになります。
暗黙の型変換のざっくり感覚
| 渡す値 | 受け取る型 | 起きること |
|---|---|---|
| 5 | double | 5.0 に変換されやすい(格上げ) |
| 2.9 | int | 2 になりやすい(小数切り捨て) |
| "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” という関係がシンプルで追いやすいです。
