ポインタ変数(2)

目次

ポインタ変数の有用性

ポインタ変数を学習していると,なんだか面倒で,できれば使いたくないと思われるかもしれない.
しかし,Cのプログラムにおいて,以下のようなポインタ変数の使用が必須,またはポインタ変数を使用することが望ましい場面がある.

特に,計算機の性能を最大限発揮したい場面や,ハードウエアに密接に関わる制御プログラムや,計算機システムの根幹にかかわるプログラムを書く場面では,C言語が大いに活躍している.
(逆に,機械学習や情報数理,Webなどの分野では,データ処理やアルゴリズム実装のウエイトが高いため,pythonやJavaScriptなど,特定分野の関連ライブラリが充実している言語が選択・使われることが多い.)

よくある間違い

ポインタ変数は,他の変数などを指している状態で使うのが原則なので,指す相手(=アドレス値)が定まっていないポインタを参照したり,代入などをしてはいけない.
もちろん,NULLポインタが指しているアドレスに代入することも禁止である.

#include <stdio.h>

int main(void)
{
    int *p;    // これだけでは何もさしていない!
//  int *p = NULL;    // NULLポインタ
	
    *p = 100;  // OMG !! どこに代入される?
	
    return 0;
}

タチの悪いことに,このコードはコンパイルも通るし,実行しても問題が起きない場合も多い.
このような迷子のポインタを使うことはプログラムの深刻なバグになるので,ポインタ変数は慎重に扱う必要がある.
(意図的にポインタでエラーを起こす場面は通常無いが,うっかり無効なポインタ参照をしてしまうことがよくある.)

#include <stdio.h>

int main(void)
{
    int data;

    int *p = &data;

    *p = 100;  // pはdataを指しているので,OK

    return 0;
}

練習問題

上記プログラムを実行して,どのようになるか(コンパイルエラー,実行時エラーなど)を確かめよ.

仮引数としてのポインタ変数

関数に引数を渡す方には,3種類あることは関数の回に述べたが,再度確認してみよう.

引数の渡し方

1.値渡し(call by value)
通常の変数の渡し方であり,呼び出される関数側の仮引数に,実引数の値がコピーされて渡される.
したがって,仮引数を変更しても元の実引数には影響を与えない.
関数に渡した変数の値を変更されたくない場合に使用.

2.アドレス渡し
値のコピーではなく,値のある場所(アドレス)を渡す方法である. すなわち,ポインタ変数を使用する渡し方である.
関数に引数を変更させたい場合に使用.
(細かいことを言えば,ポインタ変数が複製され,結果的に呼び出し元の変数を操作できる.)
C言語では,関数に配列や文字列を渡す場合はアドレス渡しとなる.

3.参照渡し(call by reference)
C++ の新しい機能で,呼び出し側の元のデータの「参照」が渡される.
本質はアドレス渡しであるが,ポインタ演算子*, &の記述を減らせるメリットがある.
一方,デメリットとしては,渡す側から見て関数で引数が変更されるか否か一目ではわからない.
(アドレス渡しであれば,& を明示して渡すので,変更される可能性があるとわかる.)

値渡し アドレス渡し 参照渡し(C++のみ)
#include <stdio.h>

void clear(int x)
{
    x = 0;
}

int main(void)
{
    int a = 100;

    printf("a=%d\n",a);

    clear(a);

    printf("a=%d\n",a);

    return 0;
}
Call by value
値渡しでは仮引数(別の変数)に値がコピーされる.
#include <stdio.h>

void clear(int *px) // ポインタで受け取る
{
    *px = 0;    // ポインタ変数には*をつける
}

int main(void)
{
    int a = 100;

    printf("a=%d\n",a);

    clear(&a);     // アドレスを渡す

    printf("a=%d\n",a);

    return 0;
}

Call by address
アドレス渡しでは仮引数が実引数を指すポインタとなり,実引数を操作できる.

#include <stdio.h>

void clear(int& x)   // 参照で受け取る
{
    x = 0;
}

int main(void)
{
    int a = 100;

    printf("a=%d\n",a);

    clear(a);  // 値渡しと同じ見た目

    printf("a=%d\n",a);

    return 0;
}