【C言語の重要概念】値渡しとアドレス渡しの違いとは?ポインタで引数を渡す方法を解説

C言語で関数を呼び出す際、引数として渡した変数の値が、関数の中で変更されても、呼び出し元の変数には影響がない場合があります。一方で、関数内での変更が、呼び出し元の変数に直接反映されることもあります。この違いを生むのが、C言語における引数の渡し方である**「値渡し」「アドレス渡し」**です。

この2つの違いを理解することは、C言語をマスターする上で非常に重要です。今回は、それぞれの仕組みと使い方を、分かりやすい例と共に徹底解説します。


目次

値渡し:引数の値をコピーして渡す方法

値渡しは、C言語におけるデフォルトの引数の渡し方です。関数を呼び出す際に、引数として指定した変数の値そのものがコピーされ、関数の仮引数に渡されます。


** analogy:** 値渡しは、**「書類のコピーを渡す」**ようなものです。渡された側(関数)がコピーに何か書き込んでも、元の書類(呼び出し元の変数)には何の影響もありません。

値渡しのサンプルコード

次のコードは、main関数からadd_and_print関数に変数を渡し、関数内で値を変更しても、main関数の元の変数が変わらないことを示しています。

#include <stdio.h>

// 2つの数値を受け取り、それぞれをインクリメントして合計を返す関数
int add_and_print(int val1, int val2);

int main(void) {
    int a = 10;
    int b = 20;
    int result;

    printf("【呼び出し前】main -> a: %d, b: %d\n", a, b);
    
    // aとbの「値」がadd_and_print関数にコピーされる
    result = add_and_print(a, b);
    
    printf("【呼び出し後】main -> a: %d, b: %d\n", a, b); // aとbの値は変わらない!
    printf("計算結果: %d\n", result);
    
    return 0;
}

int add_and_print(int val1, int val2) {
    printf("  【関数内・処理前】val1: %d, val2: %d\n", val1, val2);
    
    // コピーされた値(val1, val2)を変更する
    val1++;
    val2++;
    
    printf("  【関数内・処理後】val1: %d, val2: %d\n", val1, val2);
    
    return val1 + val2;
}

実行結果:

【呼び出し前】main -> a: 10, b: 20
  【関数内・処理前】val1: 10, val2: 20
  【関数内・処理後】val1: 11, val2: 21
【呼び出し後】main -> a: 10, b: 20
計算結果: 32

add_and_print関数内でval1val2の値を変更しても、main関数のabの値は全く変わっていないことが分かります。これが値渡しの特徴です。


アドレス渡し:引数のメモリアドレスを渡す方法

アドレス渡しは、値のコピーではなく、変数が格納されているメモリアドレスを関数に渡す方法です。これにより、関数は呼び出し元の変数を直接操作できるようになります。アドレスを扱うために、ポインタを利用します。


** analogy:** アドレス渡しは、**「共有フォルダにある書類のリンク(アドレス)を渡す」**ようなものです。渡された側(関数)がそのリンクから書類を編集すると、元の書類そのものが変更されます。

アドレス渡しのサンプルコード

main関数の変数を、関数内で直接変更するプログラムを見てみましょう。

  • 関数を呼び出す側: & を付けて変数のアドレスを渡す。
  • 関数を定義する側: * を付けて引数をポインタとして受け取る。
  • 関数内で値にアクセスする際: * を付けてポインタが指す先のを操作する。
#include <stdio.h>

// 2つの数値のアドレスを受け取り、値を直接変更して合計を返す関数
int add_and_modify(int *ptr1, int *ptr2);

int main(void) {
    int a = 10;
    int b = 20;
    int result;

    printf("【呼び出し前】main -> a: %d, b: %d\n", a, b);
    
    // aとbの「アドレス」をadd_and_modify関数に渡す
    result = add_and_modify(&a, &b);
    
    printf("【呼び出し後】main -> a: %d, b: %d\n", a, b); // aとbの値が変わっている!
    printf("計算結果: %d\n", result);

    return 0;
}

int add_and_modify(int *ptr1, int *ptr2) {
    printf("  【関数内・処理前】*ptr1: %d, *ptr2: %d\n", *ptr1, *ptr2);

    // ポインタが指す先の値(つまりmainのaとb)を直接変更する
    (*ptr1)++;
    (*ptr2)++;

    printf("  【関数内・処理後】*ptr1: %d, *ptr2: %d\n", *ptr1, *ptr2);

    return *ptr1 + *ptr2;
}

実行結果:

【呼び出し前】main -> a: 10, b: 20
  【関数内・処理前】*ptr1: 10, *ptr2: 20
  【関数内・処理後】*ptr1: 11, *ptr2: 21
【呼び出し後】main -> a: 11, b: 21
計算結果: 32

今度は、add_and_modify関数内での変更が、main関数のabに直接反映されていることが明確に分かります。


まとめ:値渡しとアドレス渡しの使い分け

値渡しアドレス渡し
渡すもの値のコピー値のメモリアドレス
元の変数影響を受けない(安全)影響を受ける(変更可能)
主な用途・関数が値を参照するだけでよい場合<br>・計算結果を戻り値で返す場合・関数内で複数の値を変更したい場合<br>・大きなデータ(構造体など)のコピーを防ぎたい場合

基本的な考え方:

  • 安全な「値渡し」を基本とし、関数内で呼び出し元の変数を変更する必要がある場合にのみ、「アドレス渡し」を使う。

この2つの違いを正しく理解し、適切に使い分けることが、C言語プログラミング上達の鍵となります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次