C++プログラミングにおいて、ポインタは非常に重要な概念です。
初心者の方にとっては理解が難しい部分もあるかもしれませんが、本記事ではわかりやすくポインタの使い方を解説します。
サンプルコードを交えながら、ポインタの基礎から応用まで詳しく説明していきます。
C言語プログラミングの基礎を理解している方であれば、この記事を読むことでC++におけるポインタの扱い方をマスターすることができます。
ポインタとは何か
ポインタとは、メモリ上のアドレスを格納するための変数です。
C++では、変数に対して&
演算子を用いることでその変数のアドレスを取得することができます。
また、*
演算子を用いることで、ポインタが指し示すアドレスに格納されている値を参照することができます。
例えば、以下のようなコードでは、int型の変数aに対してポインタpを宣言し、pにaのアドレスを代入しています。
int a = 10;
int *p = &a;
この場合、pはaのアドレスを指し示すポインタとなります。また、*p
という表記でpが指し示すアドレスに格納されている値(つまりaの値)にアクセスすることができます。
std::cout << *p << std::endl; // 出力結果:10
ポインタの使い方
ポインタは、変数のアドレスを格納するための特別な変数です。C++では、ポインタを宣言する方法がいくつかあります。
ポインタの初期化方法
ポインタの初期化方法について詳しく見ていきましょう。
ポインタ変数を宣言した後、その変数が指すメモリアドレスを設定する必要があります。
NULLで初期化する方法
最も一般的な初期化方法は、null_ptr
で初期化することです。
null_ptr
は、何も参照していないNULL
ポインタであることを示す定数で、C++ではポインタの初期化でNULL
ではなくnull_ptr
を使うことが多いです。
int* ptr = null_ptr;
上記の例では、int型のポインタ変数ptrをnull_ptr
で初期化しています。
アドレス演算子(&)を使って初期化する方法
アドレス演算子(&)を使って、既存の変数のアドレスからポインタ変数を作成することもできます。
int num = 10;
int* ptr =
上記の例では、int型の変数numに10を代入し、そのアドレスをint型のポインタ変数ptrに代入しています。これにより、ptrはnumが格納されているメモリアドレスを指すようになります。
new演算子を使って動的にメモリ領域を確保する方法
new演算子は、動的にメモリ領域を確保し、その先頭アドレスへのポインタを返します。以下はnew演算子を使用してint型配列用のメモリ領域を確保し、その先頭アドレスへのポインタpArrayに代入する例です。
int* pArray = new int[5];
上記の例では、int型配列用に5つ分(20バイト)のメモリ領域が動的に確保されています。pArrayはこの先頭アドレス(配列要素0番目)へのポインタとなります。 注意点としては、new
で確保したメモリ領域は必ずdelete
で解放しなければならないことです。
ポインタの参照方法
ポインタのアドレスを参照するには、アドレス演算子&
を使用します。
アドレス演算子を変数名の前に付けることで、その変数のメモリ上のアドレスを取得することができます。
例えば、以下のようなコードでは、変数num
のアドレスをポインタ変数ptr
に格納しています。
int num = 10;
int* ptr = # // `&num`でnumのアドレスを取得し、ポインタ変数`ptr`に格納
また、ポインタ変数自体もアドレスを持っており、そのアドレスを取得することもできます。これは、「ポインタのポインタ」と呼ばれます。
以下のようなコードでは、ポインタ変数ptr1
が指す先(つまり、ptr2
)のアドレスを取得しています。
int num = 10;
int* ptr1 = &num // `&num`でnumのアドレスを取得し、ポインタ変数`ptr1`に格納
int** ptr2 = &ptr1; // `&ptr1`でptr1のアドレスを取得し、ポインタ変数`ptr2`に格納
ポインタの演算
ポインタは、アドレスの数値を格納する変数です。そのため、ポインタに対して演算を行うことができます。
ポインタの加算・減算演算子
ポインタに対して加算・減算演算子を使用することで、ポインタが指すアドレスを移動させることができます。
例えば、以下のようなコードでは、p
が指すアドレスから sizeof(int)
バイト分だけ移動したアドレスを q
に代入しています。
int a[5] = {1, 2, 3, 4, 5};
int* p = &a[2];
int* q = p + sizeof(int);
この場合、p
の値は &a[2]
(つまり a
配列の3番目の要素のアドレス)であり、q
の値は &a[3]
(つまり a
配列の4番目の要素のアドレス)になります。
また、減算演算子 -
を使用することもできます。例えば、以下のようなコードでは、p
が指すアドレスから sizeof(int)
バイト分だけ戻ったアドレスを r
に代入しています。
int a[5] = {1, 2, 3, 4, 5};
int* p = &a[2];
int* r = p - sizeof(int);
この場合、p
の値は &a[2]
(つまり a
配列の3番目の要素のアドレス)であり、r
の値は &a[1]
(つまり a
配列の2番目の要素のアドレス)になります。
ポインタ同士の引き算演算子
ポインタ同士を引くことで、それらが指すメモリ領域間隔を求めることができます。例えば以下のようなコードでは、配列内で隣り合う要素間隔を求めています。
int a[5] = {1, 2, 3, 4, 5};
int* p = &a[0];
int* q = &a[1];
std::cout << "pからqまで" << q - p << "要素離れています" << std::endl;
この場合、「pからqまで」1要素離れているため、1
と表示されます。また、「qからpまで」引く場合は負数が返ってくるため注意しましょう。
ポインタの配列
ポインタの配列とは、複数のポインタを配列として扱うことです。
例えば、int型の変数を指すポインタを3つ宣言し、それらを配列に格納する場合は以下のように書きます。
int* ptrArray[3];
このように宣言することで、ptrArray[0]
、ptrArray[1]
、ptrArray[2]
の3つの要素が作成されます。各要素は int 型の変数を指すポインタです。
また、ポインタの配列は二次元配列と同様に扱うこともできます。例えば以下のようなコードでは、int型の値を持つ2次元配列を作成し、その各要素に対応するポインタを格納した配列を作成しています。
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int* ptrArr[2];
for (int i = 0; i < 2; i++) {
ptrArr[i] = arr[i];
}
cout << ptrArr[0][1]; // 出力結果: 2
このようにすることで、arr[i][j]
を ptrArr[i][j]
のようにアクセスすることができます。
ポインタを使った関数
ポインタを関数の引数として渡すことができます。これにより、関数内でポインタが指し示す値を変更することができます。
例えば、以下のようなswap関数を考えてみましょう。
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
この関数は、int型のポインタaとbを引数に取り、それらが指し示す値を交換します。
具体的には、temp変数にaが指し示す値を一時的に保存し、aが指し示す値をbが指し示す値で上書きします。
そして、bが指し示す値をtemp変数の値で上書きします。
この関数を使うと以下のようになります。
#include <iostream>
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 3;
int b = 5;
std::cout << "a = " << a << "\tb = " << b << std::endl;
swap(&a, &b);
std::cout << "a = " << a << "\tb = " << b << std::endl;
return 0;
}
a = 3 b = 5
a = 5 b = 3
このようにポインタを使って関数内で変数の値を変更することは非常に便利です。しかし、誤った使い方や扱い方によってはプログラム全体が壊れてしまう可能性もあるため注意が必要です。