説明に例を含めてください。
int *p;
整数へのポインターを定義し、*p
そのポインターを逆参照します。つまり、pが指すデータを実際に取得します。
説明に例を含めてください。
int *p;
整数へのポインターを定義し、*p
そのポインターを逆参照します。つまり、pが指すデータを実際に取得します。
回答:
それはだ、通常は十分に良い-あなたがアセンブリプログラミングをしている場合を除き-想定するポインタ 1が2、第三の3第四ので、プロセスのメモリ内に第二のバイトを参照して、数値のメモリアドレスを含むが....
ポインタが指すメモリ内のデータ/値(その数値インデックスを持つアドレスの内容)にアクセスする場合は、ポインタを逆参照します。
コンピューター言語が異なれば表記が異なり、コンパイラーまたはインタープリターに、ポイントされたオブジェクトの(現在の)値に興味があることを伝えます。以下では、CおよびC ++に焦点を当てます。
p
以下のようなポインタが与えられたCで考えてみましょう...
const char* p = "abc";
...文字「a」、「b」、「c」をエンコードするために使用される数値を含む4バイト、およびテキストデータの終わりを示す0バイトは、メモリのどこかに格納され、その数値アドレスデータはに保存されp
ます。このように、Cがメモリ内のテキストをエンコードすることをASCIIZといいます。
たとえば、文字列リテラルがたまたまアドレス0x1000にありp
、32ビットポインターが0x2000にある場合、メモリの内容は次のようになります。
Memory Address (hex) Variable name Contents
1000 'a' == 97 (ASCII)
1001 'b' == 98
1002 'c' == 99
1003 0
...
2000-2003 p 1000 hex
アドレス0x1000の変数名/識別子はありませんが、アドレスを格納するポインターを使用して文字列リテラルを間接的に参照できますp
。
文字がp
指す文字を参照するにはp
、次の表記のいずれかを使用して逆参照します(これもCの場合)。
assert(*p == 'a'); // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
// p and 1 times the size of the things to which p points:
// In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b'); // Another notation for p[1]
ポインタをポイントされたデータを介して移動し、移動中にそれらを逆参照することもできます。
++p; // Increment p so it's now 0x1001
assert(*p == 'b'); // p == 0x1001 which is where the 'b' is...
書き込むことができるデータがある場合は、次のようなことができます。
int x = 2;
int* p_x = &x; // Put the address of the x variable into the pointer p_x
*p_x = 4; // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4
上記ではx
、と呼ばれる変数が必要であることをコンパイル時に知っておく必要があり、コードはコンパイラーにそれを格納する場所を調整するように要求し、アドレスがを介して確実に利用できるようにし&x
ます。
Cでは、データメンバーを持つ構造体へのポインターである変数がある場合、->
逆参照演算子を使用してそれらのメンバーにアクセスできます。
typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159; // Dereference and access data member x.d_
(*p).d_ *= -1; // Another equivalent notation for accessing x.d_
ポインターを使用するために、コンピュータープログラムは、ポイントされているデータのタイプについての洞察も必要です。そのデータタイプが表すために複数のバイトを必要とする場合、ポインターは通常、データ内の最小番号のバイトをポイントします。
そこで、もう少し複雑な例を見てみましょう。
double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3); // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4); // Actually looks at bytes from address p + 1 * sizeof(double)
// (sizeof(double) is almost always eight bytes)
++p; // Advance p by sizeof(double)
assert(*p == 13.4); // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8; // Change sizes[3] from 19.4 to 29.8
// Note earlier ++p and + 2 here => sizes[3]
プログラムが実行され、そこにスローされるデータを確認するまで、必要なメモリ量がわからない場合があります...を使用してメモリを動的に割り当てることができますmalloc
。ポインタにアドレスを格納することは一般的な方法です...
int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
*p = 10; // Dereference the pointer to the memory, then write a value in
fn(*p); // Call a function, passing it the value at address p
(*p) += 3; // Change the value, adding 3 to it
free(p); // Release the memory back to the heap allocation library
C ++では、通常、メモリの割り当てはnew
演算子を使用して行われ、次のように割り当て解除されますdelete
。
int* p = new int(10); // Memory for one int with initial value 10
delete p;
p = new int[10]; // Memory for ten ints with unspecified initial value
delete[] p;
p = new int[10](); // Memory for ten ints that are value initialised (to 0)
delete[] p;
以下のC ++スマートポインターも参照してください。
多くの場合、ポインタは、データまたはバッファがメモリ内のどこに存在するかを示す唯一の指標である場合があります。そのデータ/バッファを継続的に使用する必要がある場合、またはメモリを呼び出す、free()
またはdelete
メモリのリークを回避する機能が必要な場合、プログラマはポインタのコピーを操作する必要があります...
const char* p = asprintf("name: %s", name); // Common but non-Standard printf-on-heap
// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
if (!isprint(*q))
*q = '_';
printf("%s\n", p); // Only q was modified
free(p);
...または変更の反転を慎重に調整します...
const size_t n = ...;
p += n;
...
p -= n; // Restore earlier value...
free(p);
C ++では、スマートポインタオブジェクトを使用してポインタを格納および管理し、スマートポインタのデストラクタが実行されたときに自動的に割り当てを解除することがベストプラクティスです。C ++ 11以降、標準ライブラリは2つを提供していunique_ptr
ます。
{
std::unique_ptr<T> p{new T(42, "meaning")};
call_a_function(p);
// The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete
...およびshared_ptr
共有所有権の場合(参照カウントを使用)...
{
auto p = std::make_shared<T>(3.14, "pi");
number_storage1.may_add(p); // Might copy p into its container
number_storage2.may_add(p); // Might copy p into its container } // p's destructor will only delete the T if neither may_add copied it
CではNULL
、0
さらにC ++ではnullptr
、ポインターが変数のメモリアドレスを現在保持していないことを示すために使用でき、逆参照したり、ポインター演算で使用したりしないでください。例えば:
const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
int c;
while ((c = getopt(argc, argv, "f:")) != -1)
switch (c) {
case f: p_filename = optarg; break;
}
if (p_filename) // Only NULL converts to false
... // Only get here if -f flag specified
CおよびC ++では、作り付けの数値型は必ずしもようにデフォルトしていないと同じように0
、またbools
にfalse
、ポインタは常にに設定されていませんNULL
。これらはすべて、static
変数または(C ++のみ)静的オブジェクトまたはそのベースの直接または間接メンバー変数であるか、ゼロの初期化(たとえばnew T();
、new T(x, y, z);
ポインターを含むTのメンバーでゼロの初期化を実行する)の場合、0 / false / NULLに設定されます。new T;
ではない)。
さらに、あなたが割り当てるとき0
、NULL
およびnullptr
ポインタへのポインタ内のビットは、必ずしも全てリセットされません:ポインタは、ハードウェアレベルで「0」を含む、またはあなたの仮想アドレス空間内のアドレス0を参照しない場合があります。コンパイラは、それが理由を持っている場合があり、他店のものに許可されていますが、何でもそれはない-あなたが一緒に来て、へのポインタを比較する場合0
、NULL
、nullptr
予想通りまたはそれらのいずれかを割り当てられていた別のポインタ、比較が働かなければなりません。したがって、コンパイラー・レベルのソース・コードの下では、CおよびC ++言語では「NULL」は少し「魔法の」可能性があります...
より厳密には、初期化されたポインタは、NULL
または(多くの場合仮想)メモリアドレスを識別するビットパターンを格納します。
単純なケースは、これがプロセスの仮想アドレス空間全体への数値オフセットである場合です。より複雑なケースでは、ポインタは特定のメモリ領域に関連している可能性があります。CPUは、CPUの「セグメント」レジスタまたはビットパターンでエンコードされたセグメントIDのいくつかの方法に基づいて選択したり、アドレスを使用したマシンコード命令。
たとえばint*
、int
変数をポイントするように適切に初期化すると、-にキャストした後-変数が存在float*
するメモリとはまったく異なる「GPU」メモリ内のメモリにアクセスし、int
キャストして関数ポインタとして使用すると、さらにポイントする可能性があります。プログラムのマシンオペコードを保持する個別のメモリ(int*
これらのメモリ領域内のランダムで無効なポインタの数値を使用)。
CやC ++などの3GLプログラミング言語は、次のような複雑さを隠す傾向があります。
コンパイラーが変数または関数へのポインターを提供する場合、(変数が破壊されたり、割り当てが解除されない限り)自由に逆参照することができます。たとえば、特定のCPUセグメントレジスタを事前に復元する必要があるかどうかなど、コンパイラーの問題です。使用される別個の機械語命令
配列内の要素へのポインターを取得した場合、ポインター演算を使用して、配列内の他の場所に移動したり、要素への他のポインターと比較して正当な配列の1つ前のアドレスを作成したりできます配列内(または、ポインター演算によって同じように最後の1つ前の値に移動されたもの); 再びCとC ++では、これが確実に機能するかどうかはコンパイラ次第です。
共有メモリマッピングなどの特定のOS関数はポインタを提供する場合があり、それらはそれらにとって意味のあるアドレスの範囲内で「正常に動作」します
これらの境界を超えて正当なポインターを移動する試み、ポインターに任意の数をキャストする試み、または無関係な型にキャストされるポインターを使用する試みは、通常、未定義の動作をするため、高レベルのライブラリーおよびアプリケーションでは回避する必要がありますが、OS、デバイスドライバーなどのコード。CまたはC ++標準では定義されていない動作に依存する必要があるかもしれませんが、特定の実装またはハードウェアによって定義されています。
p[1]
と*(p + 1)
同じ?つまり、同じ命令p[1]
を*(p + 1)
生成しますか?
p
は2000だけです。p
それへの別のポインタがある場合、4または8バイトに2000を格納する必要があります。お役に立てば幸いです。乾杯。
u
に配列が含まれている場合、arr
gccとclangはどちらも、左辺値u.arr[i]
が他の共用体メンバーと同じストレージにアクセスする可能性があることを認識しますが、左辺値がアクセスする可能性があることは認識し*(u.arr+i)
ません。それらのコンパイラの作者が後者がUBを呼び出すと考えるのか、それとも前者がUBを呼び出すと考えるのかはわかりませんが、いずれにせよそれを有効に処理する必要がありますが、2つの式が異なると明確に見なしています。
ポインターの逆参照とは、ポインターが指すメモリー位置に保管されている値を取得することを意味します。演算子*はこれを行うために使用され、逆参照演算子と呼ばれます。
int a = 10;
int* ptr = &a;
printf("%d", *ptr); // With *ptr I'm dereferencing the pointer.
// Which means, I am asking the value pointed at by the pointer.
// ptr is pointing to the location in memory of the variable a.
// In a's location, we have 10. So, dereferencing gives this value.
// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.
*ptr = 20; // Now a's content is no longer 10, and has been modified to 20.
[]
もポインタを逆参照することを追加します(a[b]
はと定義されています*(a + b)
)。
ポインタは値への「参照」です。ライブラリの呼び出し番号が本への参照によく似ています。呼び出し番号を「逆参照」すると、その本が物理的に通過して取得されます。
int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;
// The * causes pA to DEREFERENCE... `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4..
本がそこにない場合、司書は叫び始め、図書館を閉鎖します、そして、何人かの人々はそこにない本を見つけようとする人の原因を調査するように設定されています。
ポインタの基本からのコードと説明:
間接参照操作はポインターから始まり、矢印をたどってその指示先にアクセスします。目標は、指示先の状態を確認すること、または指示先の状態を変更することです。ポインターの間接参照操作は、ポインターに指示先がある場合にのみ機能します。指示先を割り当て、ポインターがそれを指すように設定する必要があります。ポインターコードの最も一般的なエラーは、指示先の設定を忘れていることです。コード内のそのエラーが原因で最も一般的なランタイムクラッシュは、失敗した逆参照操作です。Javaでは、不正な逆参照はランタイムシステムによって丁寧にフラグされます。C、C ++、Pascalなどのコンパイルされた言語では、誤った逆参照がクラッシュしたり、微妙なランダムな方法でメモリが破損したりすることがあります。
void main() {
int* x; // Allocate the pointer x
x = malloc(sizeof(int)); // Allocate an int pointee,
// and set x to point to it
*x = 42; // Dereference x to store 42 in its pointee
}
以前の答えはすべて間違っていると思います。逆参照は実際の値にアクセスすることを意味するからです。代わりにウィキペディアは正しい定義を提供します:https : //en.wikipedia.org/wiki/Dereference_operator
ポインター変数を操作し、ポインターアドレスの値と同等のl値を返します。これはポインタの「逆参照」と呼ばれます。
とはいえ、ポインターが指す値にアクセスすることなく、ポインターを逆参照することができます。例えば:
char *p = NULL;
*p;
値にアクセスせずにNULLポインターを逆参照しました。または、次のようにすることもできます。
p1 = &(*p);
sz = sizeof(*p);
この場合も、逆参照しますが、値にはアクセスしません。このようなコードはクラッシュしません。実際に無効なポインタでデータにアクセスすると、クラッシュが発生します。ただし、残念なことに、標準によれば、実際のデータに触れようとしない場合でも、無効なポインターの逆参照は(いくつかの例外を除いて)未定義の動作です。
つまり、ポインタの逆参照とは、逆参照演算子をポインタに適用することを意味します。その演算子は、将来使用するためにl値を返すだけです。
*p;
未定義の動作を引き起こします。逆参照が値自体にアクセスしないことは正しいですが、コード*p;
は値にアクセスします。