C/C++ の関数における値渡し、ポインタ渡しおよび参照渡しについて

ポインタ

C/C++ において、int x = 5 を実行したときに、メモリ上のどこかで変数の実体(値)が保存され、下図では値 5 はメモリ 0xAF003A に保存されている。その値の名前が x と命名される。そのため、x と名付けられたオブジェクトからは、実体と住所の 2 つのデータを取り出すことができる。

C/C++ポインタの概念

何らかのオブジェクトのアドレスを取り出して、そのアドレスを別の変数に保存したい場合は、ポインタ変数を利用する。

ポインタの宣言と初期化

変数からは、変数が保持している実体(値)と変数が所在しているメモリ上の住所の 2 つの値を取り出すことができる。変数の実体を取り出すには、変数の名前をそのまま使えばいい。一方で、変数のメモリ上の住所を取り出すには、アドレス演算子 & を使う必要がある。

次のサンプルコードは、まず文字列変数 seq を生成し、その変数に保存されている値およびその変数のメモリ上の住所を取り出して、出力している例である。

#include <iostream>
#include <string>
#include <vector>

int main(void) {    
    std::string seq = "TGATCGATCGTA";
    
    // value
    std::cout << seq << std::endl;
    // TGATCGATCGTA

    // address on the memory
    std::cout << &seq << std::endl;
    // 0x7fff5c9cc810

    return 0;
}

変数のアドレスを保存するには、その変数の型にあったポインタ変数を使う必要がある。例えば、文字列型の変数のアドレスを保存するには、文字列型のポインタを利用する。ポインタ変数は、メモリ上のアドレスを保存しているので、そのアドレスに格納されている実体(値)を取り出すには、間接参照演算子 * を利用する。アドレス演算子 & と間接参照演算子 * は互いに逆関数のようなものである。

#include <iostream>
#include <string>
#include <vector>

int main(void) {    
    std::string seq = "TGATCGATCGTA";
    std::string* seq_address = &seq;
    
    std::cout << seq << std::endl;
    // TGATCGATCGTA
    std::cout << &seq << std::endl;
    // 0x7fff5c9cc810

    std::cout << seq_address << std::endl;
    // 0x7fff5c9cc810
    std::cout << *seq_address << std::endl;
    // TGATCGATCGTA

    return 0;
}

関数とポインタ

関数の引数として、値を渡す方法とポインタを渡す方法などがある。値渡しの場合は、変数の値のコピーを関数に渡すので、関数内でその変数の値を変更してても、関数を呼び出した側の値は変更されることはない。一方で、ポインタ渡しの場合は、変数の住所そのものを、関数内でその変数の値を変更してしまうと、その関数を抜けた後も、値は変更されたままにある。

#include <iostream>
#include <string>
#include <vector>

void f(int x) {
    x += 1;
}

void g(int* x) {
    *x += 1;
}

int main(void) {
    int x = 10;

    std::cout << x << std::endl;
    // 10

    f(x);

    std::cout << x << std::endl;
    // 10

    g(&x);

    std::cout << x << std::endl;
    // 11

    return 0;
}

関数の引数として、値を渡す方法とポインタを渡す方法のほかに、C++ で追加された機能として参照渡しがある。機能的に、ポインタ渡しとほぼ同じである。

#include <iostream>
#include <string>
#include <vector>

void f(int x) {
    x += 1;
}

void h(int& x) {
    x += 1;
}

int main(void) {
    int x = 10;

    std::cout << x << std::endl;
    // 10

    f(x);

    std::cout << x << std::endl;
    // 10

    h(x);

    std::cout << x << std::endl;
    // 11

    return 0;
}