↓↓クリックして頂けると励みになります。
【13 | 関数の定義】 << 【ホーム】 >> 【15 | 関数の宣言】
C++におけるポインター(pointer)は、メモリ内の別の変数やオブジェクトのアドレスを格納するための変数です。
ポインターは、メモリの特定の場所を指し示すために使用されます。
C++のポインターは、次のような特徴を持ちます:
- メモリアドレスの格納: ポインター変数は、他の変数やオブジェクトのメモリアドレスを格納します。これにより、変数やオブジェクトの実際のデータが格納されている場所を示すことができます。
- 間接参照: ポインターを使用して、そのポインターが指し示すメモリ位置に格納されたデータに直接アクセスすることができます。これをポインターの「間接参照」と呼びます。これにより、動的なメモリ割り当てやデータの共有など、多くの便利な機能を実現できます。
- ポインター演算: ポインターには、加算、減算、比較などの演算を行うことができます。これにより、配列の要素へのアクセスや、メモリブロックの操作が容易になります。
ポインターを使って特定の変数やオブジェクトにアクセスするには、その変数やオブジェクトのメモリアドレスをポインターに割り当てます。
そして、ポインターを使って間接参照を行います。
ポインターはC++において非常に強力であり、動的メモリの割り当てや、データ構造の効率的な操作、関数への参照渡し(call by reference)など、多くの場面で使用されます。
しかし、誤った使用はメモリリークやプログラムの未定義動作を引き起こす可能性があるため、注意が必要です。
Visual Studio Codeで以下のcppファイルを作成して下さい。
新規作成 【PointerTest1.cpp】
#include <iostream> #include <stdlib.h> int main() { int a, *pa; float x, *px; a = 9; x = 2.5; pa = &a; px = &x; std::cout << "変数a のアドレスは、" << pa << "です。" << std::endl; std::cout << "変数x のアドレスは、" << px << "です。" << std::endl; pa++; px++; std::cout << "変数a の次のアドレスは、" << pa << "です。" << std::endl; std::cout << "変数x の次のアドレスは、" << px << "です。" << std::endl; }
コンピュータでは変数の値はすべて「メモリ」に格納されています。
C++ではメモリの位置を直接表す「ポインタ」という機能が用意されています。
この格納されているメモリの位置を表すのが「アドレス」と呼ばれるものです。
変数の値が格納されているメモリのアドレスを知るには、アドレス演算子の「&
」を使います。
& 変数名
アドレスは16進数で表示されます。
例えば
int a=5;
として
cout << &a << endl;
とすれば変数 a のアドレスを表示することができます。
このようにアドレスを使ってメモリ上の「位置」を表すことができます。
次に「ポインタ」について説明します。
ポインタはアドレスを格納するための特殊な変数のことです。
まずポインタの宣言の仕方について見ていきましょう。
型名 *ポインタ名;
例: int *pa;
この例の意味は「int型の変数のアドレスを格納できるポインタ変数 pa」となります。
使い方は次のようになります。
1.ポインタを宣言します。「int *pa;
」
2.変数aのアドレスをpaに格納します。「pa = &a;
」
このアドレスはインクリメントやデクリメントすることで次のアドレスやすぐ前のアドレスを知ることもできます。
もちろん「pa+1
」などとしても同じです。
サンプルプログラムを実行してみましょう。
~/Desktop/Programming/CPP $ cd "/Users/**/Desktop/Programming/CPP/" && g++ PointerTest1.cpp -o PointerTest1 && "/Users/**/Desktop/Programming/CPP/"PointerTest1 変数a のアドレスは、0x7ff7b87031fcです。 変数x のアドレスは、0x7ff7b87031ecです。 変数a の次のアドレスは、0x7ff7b8703200です。 変数x の次のアドレスは、0x7ff7b87031f0です。
Visual Studio Codeで以下のcppファイルを作成して下さい。
新規作成 【PointerTest2.cpp】
#include <iostream> #include <stdlib.h> int main() { const char *s="abcde"; const char *p; for(p=s; *p!='\0'; p++){ s++; } p=s-1; std::cout << "最後の文字は " << *p << std::endl; }
文字型(char型)のポインタ変数がどの様に扱われるかを説明します。
文字型のポインタを使えば文字列を表すことができます。
文字列とは「こんにちは」や「this」などといった文字の集まりのことで、コンピュータでは「文字」とは区別されます。
文字はシングルクォーテーション(’’
)で囲んでいたのに対し、文字列はダブルクォーテーション(””
)で囲み宣言します。
const char *s="abcde";
これにより文字型のポインタ変数s に文字列「abcde」が格納されました。
「const」を指定することによりポインタ変数の書き換えを禁止しています。
文字列をポインタ変数に格納する場合、「const」を指定せずに書き換えを許可してしまうとコンパイル時に「error C2440: '初期化中': 'const char [6]' から 'char *' に変換できません。
」というようなエラーが出ます。
このまま「std::cout << *s << std::endl;
」としたら「a」としか出力されません。
何故そのようになるのでしょうか。
文字型のポインタ変数 s にはアドレスが入っています。
文字列を代入する場合、ポインタ変数には一番最初の文字のアドレスのみが格納されます。
「std::cout << *s << std::endl;
」の 「*s
」の意味は、アドレスが示す変数を表示するという意味なので「a
」のみがが出力されるわけです。
「std::cout << s << std::endl;
」の様にすれば文字「a
」が入っているアドレスを16進数で表示できます。
では残りの「bcde
」はどこへ行ったのでしょうか。
実は「b」は「a」の次のアドレスに、「c」は「b」の次のアドレスに格納されているのです。
ですから、s+1
としてから「std::cout << *s << std::endl;
」を実行すれば「b
」が出力されます。
C言語では文字列の終わりを表す印として「¥0
」という目印を最後に書き込む決まりになっています。
「char *s="abcde";
」としても実際は「abcde¥0
」になっているのです。
このサンプルプログラムはそのことを利用して最後の文字のみを出力します。
for文で文字が¥0
になるまでアドレスsをインクリメントしていきループさせます。
そして「*s='¥0'
」になったらループを抜けます。
ここでアドレスsは最後の文字「¥0
」を指していますので、もう一つ用意していた空のポインタ変数pに「p=s-1
」として一つ前のアドレスを代入します。
この一つ前のアドレスはもちろん「e」のことですから「std::cout << *p << std::endl;
」の結果は「e」となります。
サンプルプログラムを実行してみましょう。
最後の文字は e とターミナルに出力されます。
~/Desktop/Programming/CPP $ cd "/Users/heyjude/Desktop/Programming/CPP/" && g++ PointerTest2.cpp -o PointerTest2 && "/Users/h eyjude/Desktop/Programming/CPP/"PointerTest2 最後の文字は e
Visual Studio Codeで以下のcppファイルを作成して下さい。
新規作成 【PointerTest3.cpp】
#include <iostream> int main() { int a = 5; int b = 10; int *pa; pa = &a; std::cout << "変数a の値は" << a << "です。" << std::endl; std::cout << "ポインタpaの値は" << pa << "です。" << std::endl; std::cout << "*paの値は" << *pa << "です。" << std::endl; pa = &b; std::cout << "変数b の値は" << b << "です。" << std::endl; std::cout << "ポインタpaの値は" << pa << "に変更されました。" << std::endl; std::cout << "*paの値は" << *pa << "です。" << std::endl; }
このプログラムでは、アドレスのみを代入し直していますが、ポインタ変数の値も変わっているのがわかると思います。
この関係はポインタの基本となりますので覚えておいてください。
~/Desktop/Programming/CPP $ cd "/Users/**/Desktop/Programming/CPP/" && g++ PointerTest3.cpp -o PointerTest3 && "/Users/**/Desktop/Programming/CPP/"PointerTest3 変数a の値は5です。 ポインタpaの値は0x7ff7b50d71fcです。 *paの値は5です。 変数b の値は10です。 ポインタpaの値は0x7ff7b50d71f8に変更されました。 *paの値は10です。
Visual Studio Codeで以下のcppファイルを作成して下さい。
新規作成 【PointerTest4.cpp】
#include <iostream> int main() { int a; int *pa; a = 5; pa = &a; std::cout << "変数a の値は" << a << "です。" << std::endl; *pa = 50; std::cout << "*paに50を代入しました。" << std::endl; std::cout << "変数a の値は" << a << "です。" << std::endl; }
このプログラムでは「間接参照演算子*
」を使ってポインタが示す変数に値を代入しています。
このような仕組みが役立つのはもっと先のことですが、このようなことが出来るということを覚えておいてください。
~/Desktop/Programming/CPP $ cd "/Users/**/Desktop/Programming/CPP/" && g++ PointerTest4.cpp -o PointerTest4 && "/Users/**/Desktop/Programming/CPP/"PointerTest4 変数a の値は5です。 *paに50を代入しました。 変数a の値は50です。
Visual Studio Codeで以下のcppファイルを作成して下さい。
新規作成 【PointerTest5.cpp】
#include <iostream> #include <stdlib.h> int main() { const char *s="C++ benkyouchuu"; const char *p; const char *n; n=s; for(p=s; *p!='\0'; p++){ s++; } for (p=s; n!=p; p--) { std::cout << *(p-1); } std::cout << std::endl; }
「C++ benkyouchuu
」という文字列をポインタで逆順に出力するようにしたプログラムです。
まず*s
と*p
と*n
の3つのchar型ポインタ変数を用意し、*s
に「C++ benkyouchuu
」という文字列を格納しておきます。
アドレス s は最初、この文字列の先頭「C」を指しています。
空のアドレス n に この先頭のアドレス s を格納しておきます。
その後for文で最後の文字「¥0
」までアドレスを進ませます。
次のfor文で、空のアドレスpに 文字列の最後尾のアドレス s を格納し、「std::cout << *(p-1);
」として画面に出力していきます。
1つのループがまわるたびに「p--;
」としてアドレスpから1を引いていきます。
1を引くとpは1つ前の文字を指すようになります。
nには文字列の先頭のアドレスが入っていますので「n!=p(nとpがイコールでないとき)
」はtrueとなりループしますがイコールになったとき、つまりpのアドレスが文字列の先頭のアドレスと同じになったときにループが終了しプログラムも終了します。
~/Desktop/Programming/CPP $ cd "/Users/**/Desktop/Programming/CPP/" && g++ tempCodeRunnerFile.cpp -o tempCodeRunnerFile && "/Users/**/Desktop/Programming/CPP/"tempCodeRunnerFile uuhcuoykneb ++C
【13 | 関数の定義】 << 【ホーム】 >> 【15 | 関数の宣言】
↓↓クリックして頂けると励みになります。