1

私は最近、C ++の参照とポインターを実際に理解しようとしましたが、少し混乱しています。アドレスで値を取得し、値のアドレスを取得できる演算子*と演算子は理解していますが、これらをsのような基本的な型で単純に使用できないのはなぜですか?&int

たとえば、次のようなことを実行できず、奇妙なポインタ変数の作成を使用できない理由がわかりません。

string x = "Hello";
int y = &x; //Set 'y' to the memory address of 'x'
cout << *y; //Output the value at the address 'y' (which is the memory address of 'x')

上記のコードは、理論的には「x」の値を出力するはずです。「y」には「x」のメモリアドレスが含まれているため、「*y」は「x」である必要があります。これが機能する場合(ちなみにコンパイルしようとしても機能しません-文字列からintに変換できないことを示しています。これは、メモリアドレスがint罰金に保存されます)。

なぜ特別なポインタ変数宣言(例string *y = &x)を使用する必要があるのですか?そして、この中*で、上記の行の例で文字通りポインタ宣言の演算子を使用すると、「y」の値を「x」のメモリアドレスに設定しますが、後で値にアクセスするときにメモリアドレス('&x')では、以前にメモリアドレスに設定したものと同じ'*y'を使用できます。

4

6 に答える 6

3

CおよびC++は、実行時ではなくコンパイル時に型情報を解決します。実行時のポリモーフィズムでさえ、コンパイル時にオフセットが固定された関数ポインターのテーブルを作成するコンパイラーに依存しています。

そのため、プログラムが文字列を出力していることを知る唯一の方法は、が文字列へのポインタ()として強く型付けされているcout << *y;ためです。プログラムは、アドレスだけから、アドレスに格納されているオブジェクトがであると判断することはできません。(C ++ RTTIでもこれは許可されていませんが、多態的な基本クラスを識別するのに十分な型情報が必要です。)ystd::string*ystd::string

于 2012-04-21T16:52:22.903 に答える
2

要するに、Cは型付き言語です。任意のものを変数に格納することはできません。

ウィキペディアで型安全性の記事を確認してください。C / C ++は、オペランドと関数パラメーターの型をチェックすることにより、コンパイル時に問題のある操作と関数呼び出しを防ぎます(ただし、明示的なキャストを使用すると、式の型を変更できることに注意してください)。

文字列を整数で格納することは意味がありません->ポインタを格納することは意味がないのと同じ方法です。

于 2012-04-21T16:48:40.737 に答える
2

簡単に言えば、メモリアドレスにはポインタ型があります。ポインタはintではないため、int変数にポインタを格納することはできません。intとpointersが代替可能でない理由に興味がある場合は、それぞれのサイズが実装で定義されており(特定の制限があります)、同じサイズになる保証はありません。

たとえば、@ Damien_The_Unbelieverが指摘したように、64ビットシステムのポインタは64ビット長である必要がありますが、intが32ビットであるのは、長さが長くなく、短くない限り、完全に合法です。短い。

各データ型が独自のポインタ型を持っている理由については、各型(特にユーザー定義型)がメモリ内で異なる構造になっているためです。タイプレス(またはvoid)ポインターを逆参照する場合、そのデータをどのように解釈するかを示す情報はありません。一方、ユニバーサルポインタを作成し、型を指定するという「不便さ」をなくす場合は、メモリ内の各エンティティをその型情報と一緒に格納する必要があります。これは実行可能ですが、効率的とはほど遠いものであり、効率性はC++の設計目標の1つです。

于 2012-04-21T16:53:28.457 に答える
1

C ++は強く型付けされた言語であり、ポインターと整数は異なる型です。これらの個別の型を作成することにより、コンパイラーは誤用を検出し、実行していることが正しくないことを通知できます。

同時に、ポインター型は、指定されたオブジェクトの型に関する情報を保持します。doubleのアドレスを取得する場合は、それをに格納する必要があります。double*コンパイラーは、そのポインターを逆参照すると、に到達することを認識していますdouble。サンプルコードでint y = &x; cout << *y;は、コンパイラは何y を指しているのかという情報を失い、式のタイプは不明になり、のさまざまなオーバーロードのどれを呼び出す*yかを判別できなくなります。コンパイラがそれを認識している場所operator<<と比較すると、それがaであり、それを逆参照すると(doubleやその他のタイプではなく)、コンパイラがを含むすべての式を静的にチェックできるようになります。std::string *y = &x;ystd::string*std::stringy

最後に、ポインタは単なるオブジェクトのアドレスであり、整数型(64ビットアーキテクチャではではなく)で表現できる必要があると考えていますが、常にそうであるとは限りません。ポインタが整数値で実際に表現できないさまざまなアーキテクチャがあります。たとえば、セグメント化されたメモリを備えたアーキテクチャでは、オブジェクトのアドレスにセグメント(整数値)とセグメントへのオフセット(別の整数値)の両方を含めることができます。他のアーキテクチャでは、ポインタのサイズは他の整数型のサイズとは異なりました。int64int

于 2012-04-21T17:01:06.253 に答える
1

機械語のようないくつかの非常に低レベルの言語は、あなたが説明したとおりに動作します。数字は数字であり、それが何を表すかを頭に抱えるのはプログラマー次第です。一般的に言えば、高級言語の希望は、そのスタイルの開発に起因する懸念やエラーの可能性からあなたを遠ざけることです。

危険にさらされている場合、実際にはC++の型安全性を無視することができます。たとえば、私が持っている32ビットマシンのgccは、これを実行すると「Hello」と出力します。

string x = "Hello";
int y = reinterpret_cast<int>(&x);
cout << *reinterpret_cast<string*>(y) << endl;

しかし、他のほとんどすべての回答者が指摘しているように、それが別のコンピューターで動作するという保証はありません。これを64ビットマシンで試してみると、次のようになります。

エラー:「std ::string*」から「int」にキャストすると精度が失われます

これを次のように変更することで回避できますlong

string x = "Hello";
long y = reinterpret_cast<long>(&x);
cout << *reinterpret_cast<string*>(y) << endl;

C ++標準では、これらの型の最小値が指定されていますが、最大値は指定されていないため、新しいコンパイラーに直面したときに何を処理するのかは実際にはわかりません。参照:C ++標準では、int、long型のサイズはどのように規定されていますか?

したがって、このルートを開始し、言語の安全性を「捨てる」と、移植性のないコードを作成する可能性が高くなります。 reinterpret_cast最も危険なタイプの鋳造です...

static_cast、dynamic_cast、const_cast、およびreinterpret_castはいつ使用する必要がありますか?

しかし、興味がある場合に備えて、これは技術的には「なぜintではないか」の部分を具体的に掘り下げているだけです。@BenVoightが以下のコメントで指摘しているように、C99の時点で、任意のポニターを保持することが保証されているintptr_tと呼ばれる整数型が存在することに注意してください。したがって、タイプ情報を破棄する場合、精度を失うよりもはるかに大きな問題があります...誤って間違ったタイプにキャストバックするなどです!

于 2012-04-21T17:22:34.970 に答える
0

この言語は、ハードウェアレベルでは両方とも単なるビットのセットであるにもかかわらず、2つの異なる概念を混同しないように保護しようとしています。

デバッガーのさまざまな部分の間で手動で値を渡す必要がある場合を除いて、数値を知る必要はありません。

配列の古風な使用法以外では、ポインタに「10を追加」することは意味がありません。したがって、配列を数値として扱うべきではありません。

コンパイラが型情報を保持intすることで、間違いを防ぐこともできます。すべてのポインタが等しい場合、コンパイラは、間接参照しようとしているものが。へのポインタであることを指摘できませんstring

于 2012-04-21T16:57:10.543 に答える