サーチ…
前書き
ポインタは、メモリ内の場所を参照するアドレスです。それらは一般に、関数やデータ構造が参照されたメモリをコピーすることなく、メモリの認識と修正を可能にするために使用されます。ポインタは、プリミティブ(組み込み)型またはユーザー定義型の両方で使用できます。
ポインタは、 "dereference" *
、 "address of" &
"and" arrow " ->
演算子を使用します。 '*'と ' - >'演算子は、指し示されているメモリにアクセスするために使用され、 &
演算子は、メモリ内のアドレスを取得するために使用されます。
構文
- <データ型> * <変数名>;
- <データ型> * <変数名> =&<同じデータ型の変数名>;
- <データ型> * <変数名> = <同じデータ型の値>;
- int * foo; //整数値を指すポインタ
- int * bar =&myIntVar;
- ロング*バー[2]。
- 長い* bar [] = {&myLongVar1、&myLongVar2}; //等しい:long * bar [2]
備考
同じ行に複数のポインタを宣言するときの問題に注意してください。
int* a, b, c; //Only a is a pointer, the others are regular ints.
int* a, *b, *c; //These are three pointers!
int *foo[2]; //Both *foo[0] and *foo[1] are pointers.
ポインタの基礎
注:以下のすべてでは、C ++ 11定数nullptr
が前提となっています。以前のバージョンでは、 nullptr
をNULL
に置き換えてください。これは、同じような役割を果たしていた定数です。
ポインタ変数の作成
ポインタ変数は、特定の*
構文を使用して作成することができます(例int *pointer_to_int;
。
変数がポインタ型 ( int *
)のときは、メモリアドレスだけが格納されます。メモリアドレスは、 基になる型 ( int
)のデータが格納される場所です。
違いは、変数のサイズを同じ型へのポインタのサイズと比較すると明らかです。
// Declare a struct type `big_struct` that contains
// three long long ints.
typedef struct {
long long int foo1;
long long int foo2;
long long int foo3;
} big_struct;
// Create a variable `bar` of type `big_struct`
big_struct bar;
// Create a variable `p_bar` of type `pointer to big_struct`.
// Initialize it to `nullptr` (a null pointer).
big_struct *p_bar0 = nullptr;
// Print the size of `bar`
std::cout << "sizeof(bar) = " << sizeof(bar) << std::endl;
// Print the size of `p_bar`.
std::cout << "sizeof(p_bar0) = " << sizeof(p_bar0) << std::endl;
/* Produces:
sizeof(bar) = 24
sizeof(p_bar0) = 8
*/
別の変数のアドレスを取得する
ポインタは、通常の変数と同じようにお互いに割り当てることができます。この場合、ポインタが指す実際のデータではなく、あるポインタから別のポインタにコピーされるのはメモリアドレスです。
さらに、ヌルメモリー位置を表すnullptr
値を取ることができます。 nullptr
に等しいポインタに無効なメモリ位置が含まれているため、有効なデータを参照していません。
与えられた型の変数のメモリアドレスは、演算子&
アドレスに接頭辞を付けることによって得ることができます。 &
によって返される値は、変数のメモリアドレスを含む基になる型へのポインタです( 変数がスコープから外れない限り有効なデータです)。
// Copy `p_bar0` into `p_bar_1`.
big_struct *p_bar1 = p_bar0;
// Take the address of `bar` into `p_bar_2`
big_struct *p_bar2 = &bar;
// p_bar1 is now nullptr, p_bar2 is &bar.
p_bar0 = p_bar2;
// p_bar0 is now &bar.
p_bar2 = nullptr;
// p_bar0 == &bar
// p_bar1 == nullptr
// p_bar2 == nullptr
参考文献とは対照的に:
- 2つのポインタを割り当てても、割り当てられたポインタが参照するメモリは上書きされません。
- ポインタはnullでもかまいません。
- オペレータのアドレスは明示的に要求されます。
ポインタの内容へのアクセス
アドレスには&
が必要なので、コンテンツにアクセスするには接頭辞として逆参照演算子 *
使用する必要があります。ポインタが逆参照されると、それは基底型の変数になります(実際にはその参照)。その後、 const
なくてもそれを読み取って変更することができます。
(*p_bar0).foo1 = 5;
// `p_bar0` points to `bar`. This prints 5.
std::cout << "bar.foo1 = " << bar.foo1 << std::endl;
// Assign the value pointed to by `p_bar0` to `baz`.
big_struct baz;
baz = *p_bar0;
// Now `baz` contains a copy of the data pointed to by `p_bar0`.
// Indeed, it contains a copy of `bar`.
// Prints 5 as well
std::cout << "baz.foo1 = " << baz.foo1 << std::endl;
*
と演算子の組み合わせ.
->
と略記され->
:
std::cout << "bar.foo1 = " << (*p_bar0).foo1 << std::endl; // Prints 5
std::cout << "bar.foo1 = " << p_bar0->foo1 << std::endl; // Prints 5
無効なポインタを参照解除する
ポインタを参照解除するときは、有効なデータを指していることを確認する必要があります。無効なポインタ(またはヌルポインタ)を参照解除すると、メモリアクセス違反が発生したり、ガベージデータを読み書きしたりする可能性があります。
big_struct *never_do_this() {
// This is a local variable. Outside `never_do_this` it doesn't exist.
big_struct retval;
retval.foo1 = 11;
// This returns the address of `retval`.
return &retval;
// `retval` is destroyed and any code using the value returned
// by `never_do_this` has a pointer to a memory location that
// contains garbage data (or is inaccessible).
}
このようなシナリオでは、 g++
とclang++
正しく警告を発行します。
(Clang) warning: address of stack memory associated with local variable 'retval' returned [-Wreturn-stack-address]
(Gcc) warning: address of local variable ‘retval’ returned [-Wreturn-local-addr]
したがって、ポインタが関数の引数である場合、ポインタはnullになる可能性があるので注意が必要です。
void naive_code(big_struct *ptr_big_struct) {
// ... some code which doesn't check if `ptr_big_struct` is valid.
ptr_big_struct->foo1 = 12;
}
// Segmentation fault.
naive_code(nullptr);
ポインタ操作
ポインタには2つの演算子があります。アドレス演算子(&):演算対象のメモリアドレスを返します。内容の(Dereference)演算子(*):演算子で指定されたアドレスにある変数の値を返します。
int var = 20;
int *ptr;
ptr = &var;
cout << var << endl;
//Outputs 20 (The value of var)
cout << ptr << endl;
//Outputs 0x234f119 (var's memory location)
cout << *ptr << endl;
//Outputs 20(The value of the variable stored in the pointer ptr
アスタリスク(*)は、それがポインタであることを示す簡単な目的のためにポインタを宣言する際に使用されます。これを指定されたアドレスにある値を取得するために使用される逆参照演算子と混同しないでください。それらは同じ記号で表された単純に2つの異なるものです。
ポインタ演算
インクリメント/デクリメント
ポインタはインクリメントまたはデクリメントできます(接頭辞と接尾辞)。ポインターをインクリメントすると、現在ポインティングされている要素を1つ上回る要素の配列値の要素にポインタ値が進められます。ポインタを減少させると、配列内の前の要素に移動します。
ポインターが指す型が完全でない場合、ポインター演算は許可されません。 void
は常に不完全な型です。
char* str = new char[10]; // str = 0x010
++str; // str = 0x011 in this case sizeof(char) = 1 byte
int* arr = new int[10]; // arr = 0x00100
++arr; // arr = 0x00104 if sizeof(int) = 4 bytes
void* ptr = (void*)new char[10];
++ptr; // void is incomplete.
終わりの要素へのポインタがインクリメントされると、ポインタは配列の最後を過ぎた1つの要素を指し示します。そのようなポインタは逆参照することはできませんが、デクリメントすることはできます。
配列内の1つの終わりの要素へのポインタをインクリメントするか、または配列の最初の要素へのポインタを減らすと、未定義の動作になります。
非配列オブジェクトへのポインタは、ポインタ演算のために、サイズ1の配列であるかのように扱うことができます。
加算/減算
整数値をポインタに追加することができます。これらは増分として機能しますが、1ではなく特定の数で動作します。整数値はポインタから減算することもでき、ポインタの減分として機能します。インクリメント/デクリメントと同様に、ポインタは完全な型を指していなければなりません。
char* str = new char[10]; // str = 0x010
str += 2; // str = 0x010 + 2 * sizeof(char) = 0x012
int* arr = new int[10]; // arr = 0x100
arr += 2; // arr = 0x100 + 2 * sizeof(int) = 0x108, assuming sizeof(int) == 4.
ポインタの相違
同じ型への2つのポインタ間の差を計算することができます。 2つのポインタは同じ配列オブジェクト内になければなりません。さもなければ未定義の動作が起こる。
同じ配列内に2つのポインタP
とQ
がある場合、 P
が配列のi
番目の要素であり、 Q
がj
番目の要素である場合、 P - Q
はi - j
ます。結果のタイプは<cstddef>
std::ptrdiff_t
<cstddef>
std::ptrdiff_t
です。
char* start = new char[10]; // str = 0x010
char* test = &start[5];
std::ptrdiff_t diff = test - start; //Equal to 5.
std::ptrdiff_t diff = start - test; //Equal to -5; ptrdiff_t is signed.