C言語入門|配列を渡すと何が起きるか

「えっ、同じように引数で渡してるのに、結果が違うの?」
C言語を学んでいると、誰もが一度はここで立ち止まります。

普通の変数は安全そうなのに、配列を関数に渡した瞬間、
思いがけず中身が書き換わってしまう

今回はその入口として、
「変数」と「配列」をそれぞれ関数に渡したときに
何が起きているのかを、実際のコードと図を使ってやさしく確認していきましょう。

まずは実験してみよう(サンプルプログラム)

最初に、通常の変数と配列をそれぞれ関数に渡し、
関数の中で値を変更してみます。

プロジェクト名:10-2-1 ソースファイル名: sample10-2-1.c

#include <stdio.h>

void changeValue(int x)       // int型の値を受け取る
{
    x = 42;                   // 値を書き換える
}

void changeArray(int x[])     // 配列を受け取る
{
    x[0] = 42;                // 先頭要素を書き換える
}

int main(void)
{
    int score = 10;           // 通常の変数
    int points[3] = {10, 20, 30}; // 配列

    changeValue(score);
    changeArray(points);

    printf("score=%d, points[0]=%d\n", score, points[0]);
    return 0;
}

実行結果

score=10, points[0]=42

「同じように渡している」のに結果が違う理由

コードを読むと、こんなふうに感じると思います。

  • score に 10 を入れて関数に渡した
  • points の先頭にも 10 を入れて関数に渡した
  • どちらも 42 に書き換えているように見える

それなのに、結果はこうです。

対象関数内での変更main関数側の結果
通常の変数 score変更される影響なし
配列 points変更される影響あり

ここに 配列と関数の組み合わせが生むトラブルの正体があります。

引数の独立性という大原則

C言語には、基本となる大切な考え方があります。

引数の独立性(原則)

呼び出し先の関数で引数の内容を書き換えても、
呼び出し元の関数に属する変数には影響を与えない。

これは第8章で学んだ
「ローカル変数は関数ごとに独立している」という話と同じです。

通常の変数の場合

関数呼び出し時、次のことが起きています。

項目内容
渡されるもの値そのもの
実体コピーが作られる
関数内で変更するとコピーだけが変わる
呼び出し元への影響なし

図で表すと、こんなイメージです。

  • main関数の score と
  • changeValue関数の x

まったく別の箱

だから、x をどれだけ書き換えても、score は無傷です。

なのに、配列だけ独立しないのはなぜ?

ここが今回の核心です。

配列を引数に書いたつもりでも、
C言語では 配列そのものは渡されていません

配列引数で実際に渡されているもの

見た目実際の中身
int x[]配列の先頭要素のアドレス

つまり、関数はこういう情報を受け取っています。

  • 配列のコピー
    ではなく
  • 配列が置かれている場所の情報

そのため、関数内で x[0] を書き換えると、

  • 同じメモリを見ている main関数側の配列も
  • 当然、一緒に変わってしまう

というわけです。

図で見る「変数」と「配列」の決定的な違い

通常の変数を渡した場合

関数メモリ
mainscore = 10
changeValuex = 10(別コピー)

→ 独立している

配列を渡した場合

関数メモリ
mainpoints[0] = 10
changeArrayx[0] → 同じ場所

→ 同じメモリを共有している

この「共有」が、便利さと危険さを同時に生みます。

これが不具合の温床になる理由

配列を引数に使うと、

  • 呼び出したつもりのない場所が書き換わる。
  • 関数の中身を知らないと挙動が読めない。
  • 大きなプログラムほど影響範囲が見えなくなる。

といった問題が起こりやすくなります。

だから現場では、

  • 配列を渡す関数は仕様を厳密に決める。
  • 書き換えるのか、読み取り専用なのかを明確にする。

といった約束が、とても重要になるのです。

まとめ:まだ理由は完全にわからなくていい

この段階では、

  • なぜアドレスが渡るのか
  • なぜC言語はこの仕様なのか

そこまで理解できていなくても大丈夫です。

今はまず、

配列を関数に渡すと、呼び出し元の中身が変わることがある

この事実を、しっかり体感できればOKです。

次回以降の記事の解説で、
ポインタとメモリの視点から、この動きをもう一段深く見ていきます。