C/C++ ポインタ入門 > ポインタの宣言・初期化
Nobuhide Tsuda
Oct-2013
ポインタの宣言・初期化
- さて、本題の「ポインタ」である。
- くどいようだが、16進数、メモリの理解があやふやだと、
ポインタの理解は苦しくなるので、あやふやな人はリンク先でよーく勉強してね。
- 「ポインタ」とはなんらかのオブジェクトを指し、オブジェクトを間接的に参照するためのものである。
- などと、抽象的に言われてもなかなかピンと来ないのが普通だと思う。
- 本稿を読み、少しずつ理解していけば、上記文章がいつかすっきりと理解できるようになるはずだ。
- ポインタは通常の変数と同じように宣言してから使用する。
- 型 T へのポインタは「T *ポインタ名;」で宣言する。
- T という組み込み型があるわけではない。
- 実際には char, short, int などの組み込み型や、構造体・クラス名を記述する。
- 例えば char 型へのポインタ ptr は以下のように宣言する。
char *ptr;
「T* ptr;」の様に、「*」を型にくっつけてもよい
- どちらの記法が良いかは微妙である。
- 「T*」の方が型であることを強調できていいかもしれない。が、
- C/C++ では 「T* ptr1, ptr2;」と記述したとき、ptr2 がTへのポインタとならないという文法上の不備がある。
- 正しくは「T *ptr, *ptr2;」と記述する必要がある。
- 1行に、複数の変数の宣言を書かないというコーディングルールであれば、「T*」の方が型であることが明確になりいいかもしれない
ポインタは変数などのオブジェクトを指す(ポイントする)ものである。
例えば ptr が char型変数 x を指す場合は、以下のように初期化する。
char x;
char *ptr = &x; // ptr を宣言し、x のアドレスで初期化
- 上記はポインタの宣言と初期化を1行に書いているが、以下のように分けてもよい。
char x;
char *ptr; // ptr を宣言
ptr = &x; // ptr に x のアドレスを代入
& は変数のアドレスを取り出す演算子。
が、変数は宣言時に初期化する方がよい。初期化を忘れることが無いし、行数も減るからである。
ポインタ ptr が x を指すことを下図のように図示する。
実際には、「指す」とはポインタが指しているオブジェクトのアドレスを値として持つことである
上図では x のアドレスを 0x00004000 としているが、実際のアドレスは 0x00004000 になるとは限らない。
- 実際にプログラムを記述して、ポインタの値、変数のアドレスを表示してみましょう。
#include <iostream>
#include <iomanip>
#define hexformat(fill, wd) std::hex<<std::setfill(fill)<<std::setw(wd)
int main()
{
char x;
char *ptr = &x;
std::cout << "&x = 0x" << hexformat('0', 8) << (int)&x << "\n";
std::cout << "ptr = 0x" << hexformat('0', 8) << (int)ptr << "\n";
}
実行結果:
&x = 0x002FFEDC
ptr = 0x002FFEDC
上記結果は筆者の環境での実行結果である。実際に変数がどのアドレスに割りあてあられるかは環境によって異なる。
演習問題:
- 上記プログラムを読者の環境でビルド・実行し、変数xの値がどのようなアドレスに格納されているかを確認しなさい
配列要素をポイント
- 配列の要素のアドレスもポインタに格納することができる。
- char v[10]; の場合、最初の要素のアドレスは &v[0] で取得可能
- i 番目の要素のアドレスは &v[i] で取得可能
- 後で詳しく説明するが、「v+i」でも i 番目の要素のアドレスを取得できる
- 下記プログラムは、配列 v の各要素のアドレスをポインタに入れ、表示するもの
#include <iostream>
#include <iomanip>
#define hexformat(fill, wd) std::hex <<std::setfill(fill)<<std::setw(wd)
int main()
{
const int SIZE = 10;
char v[SIZE];
for(int i = 0; i < SIZE; ++i) {
char *ptr = &v[i];
std::cout << "ptr = 0x" << hexformat('0', 8) << (int)ptr << "\n";
}
}
実行結果:
ptr = 0x003dfef4
ptr = 0x003dfef5
ptr = 0x003dfef6
ptr = 0x003dfef7
ptr = 0x003dfef8
ptr = 0x003dfef9
ptr = 0x003dfefa
ptr = 0x003dfefb
ptr = 0x003dfefc
ptr = 0x003dfefd
演習問題:
- 上記プログラムの7行目、9行目の「char」を「int」に変えて、ビルド・実行しなさい
- 結果表示が何故そのようになったのかを考察しなさい
「auto」を使ったポインタ宣言
- C++11 から、型推論を行ってくれる auto が使用できる。VC++ であれば VS2010 から使用できる。
- auto は右辺値から型名を推論してくれるので便利だぞ。
- 「char *ptr = &x;」は「auto ptr = &x」と記述できるぞ。
#include <iostream>
#include <iomanip>
#define hexformat(fill, wd) std::hex<<std::setfill(fill)<<std::setw(wd)
int main()
{
char x;
auto ptr = &x;
std::cout << "&x = 0x" << hexformat('0', 8) << (int)&x << "\n";
std::cout << "ptr = 0x" << hexformat('0', 8) << (int)ptr << "\n";
}
実行結果:
&x = 0x002FFEDC
ptr = 0x002FFEDC
auto で宣言された変数が、実際にはどんな型なのかを知るには typeid(変数).name() を使うといいぞ。
#include <iostream>
int main()
{
char v[10];
auto ptr = &v[0];
std::cout << "typeid(ptr).name() = " << typeid(ptr).name() << "\n";
}
実行結果:
typeid(ptr).name() = char *
ヌルポインタ
- 値が0のポインタを「ヌルポインタ」と呼ぶ。一部では「ヌルポ」と呼ばれている。
- ヌルポインタを参照すると、プログラムがクラッシュすることがある。
前:メモリ
|上:C/C++ ポインタ入門
|次:ポインタの指す先を参照