web-dev-qa-db-ja.com

C#リファレンスとポインターの違いは何ですか?

C#参照とポインターの違いがよくわかりません。彼らは両方とも記憶の場所を指しているのではないですか?私が理解できる唯一の違いは、ポインターがそれほど賢くなく、ヒープ上の何も指すことができず、ガベージコレクションから免除され、構造体または基本型のみを参照できることです。

私が尋ねる理由の1つは、優れたプログラマーになるには、ポインターを(Cからのように)よく理解する必要があるという認識があることです。高レベルの言語を学ぶ多くの人々はこれを逃し、したがってこの弱点を持っています。

ポインタの複雑さを理解できませんか?それは基本的にメモリ内の場所への単なる参照ですよね?その場所を返し、その場所のオブジェクトと直接対話できますか?

大きなポイントを見逃していませんか?

76
Richard

C#参照は、ガベージコレクターによって再配置できますが、通常のポインターは静的です。これが、配列要素へのポインターを取得するときにfixedキーワードを使用して、移動しないようにするためです。

編集:概念的には、はい。それらはほぼ同じです。

42
Mehrdad Afshari

ポインターと参照の間には、わずかながら非常に重要な違いがあります。ポインタはメモリ内の場所を指し、参照はメモリ内のオブジェクトを指します。ポインタは、ポイントするメモリの正確性を保証できないという意味で「タイプセーフ」ではありません。

たとえば、次のコードを取ります

int* p1 = GetAPointer();

これは、GetAPointerがint *と互換性のある型を返す必要があるという意味で、タイプセーフです。しかし、* p1が実際にintを指すという保証はまだありません。 char、double、またはランダムメモリへの単なるポインタです。

ただし、参照は特定のオブジェクトを指します。オブジェクトはメモリ内で移動できますが、参照を無効にすることはできません(安全でないコードを使用しない限り)。この点で、参照はポインターよりもはるかに安全です。

string str = GetAString();

この場合、strには2つの状態の1つがあります。1)オブジェクトを指していないため、nullまたは2)有効な文字列を指しています。それでおしまい。 CLRは、これが事実であることを保証します。ポインターを使用することはできません。

118
JaredPar

参照は「抽象」ポインターです。参照を使用して算術演算を行うことはできず、その値を使用して低レベルのトリックを再生することもできません。

12
Chris Conway

まず、セマティックスで「ポインタ」を定義する必要があると思います。安全でないコードで fixed を使用して作成できるポインターを意味しますか? IntPtr はおそらくネイティブコールから取得するのでしょうか、それとも Marshal.AllocHGlobal ですか? GCHandle を意味しますか?すべては本質的に同じものです-何かが格納されているメモリアドレスの表現-それはクラス、数値、構造体、何であれ。そして、記録のために、彼らは確かに山の上にいることができます。

ポインター(上記のすべてのバージョン)は固定アイテムです。 GCはそのアドレスに何があるのか​​分からないため、オブジェクトのメモリや寿命を管理することはできません。つまり、ガベージコレクションシステムのすべての利点が失われます。オブジェクトメモリを手動で管理する必要があり、リークの可能性があります。

一方、参照は、GCが認識している「マネージポインター」です。これはまだオブジェクトのアドレスですが、GCはターゲットの詳細を認識できるようになったため、ターゲット環境を移動したり、圧縮したり、ファイナライズしたり、廃棄したり、管理環境が行うその他のすべてのことを実行できます。

実際の大きな違いは、それらをどのように、そしてなぜ使用するかです。マネージ言語のほとんどの場合、オブジェクト参照を使用します。ポインターは相互運用を行うのに便利になり、非常に高速な作業が必要になることはまれです。

編集:実際 ここに良い例があります マネージコードで「ポインタ」を使用する可能性がある場合-この場合はGCHandleですが、AllocHGlobalを使用するか、バイト配列または構造体に固定を使用することで、まったく同じことができます。私はより多くの「.NET」を感じるため、GCHandleを好む傾向があります。

6
ctacke

ポインターは、メモリアドレス空間内の場所を指します。参照はデータ構造を指します。データ構造はすべて、(メモリスペースを圧縮するために)ガベージコレクタによって常に(まあ、それほど頻繁ではなく、時々)移動しました。また、あなたが言ったように、参照のないデータ構造は、しばらくするとガベージコレクションされます。

また、ポインターは安全でないコンテキストでのみ使用できます。

5
Tamas Czinege

参照とポインターの主な違いは、ポインターはビットのコレクションであり、そのコンテンツはポインターとしてアクティブに使用されている場合にのみ重要であり、参照はビットのセットだけでなく、基礎となるフレームワークは、その存在を通知されます。メモリ内のオブジェクトへのポインタが存在し、そのオブジェクトが削除されても、ポインタが消去されない場合、ポインタの継続した存在は、それが指すメモリにアクセスする試みがない限り、または試みられるまで、害を引き起こしません。ポインターを使用しようとしていない場合、その存在を気にすることはありません。対照的に、.NETやJVMなどの参照ベースのフレームワークでは、システムが常に存在するすべてのオブジェクト参照を識別できる必要があり、存在するすべてのオブジェクト参照は常にnullであるか、適切なタイプのオブジェクト。

各オブジェクト参照は実際には2種類の情報をカプセル化することに注意してください:(1)それが識別するオブジェクトのフィールドの内容、および(2)同じオブジェクトへの他の参照のセット。システムがオブジェクトに存在するすべての参照を迅速に識別できるメカニズムはありませんが、オブジェクトに存在する他の参照のセットは、参照によってカプセル化された最も重要なものであることがよくあります(これは特にそうですタイプObjectのものは、ロックトークンのようなものとして使用されます)。システムはGetHashCodeで使用するために各オブジェクトのデータを数ビット保持しますが、オブジェクトはそれらに存在する参照のセットを超えて実際のIDを持ちません。 Xがオブジェクトへの唯一の既存の参照を保持している場合、Xを同じフィールドの内容を持つ新しいオブジェクトへの参照に置き換えても、GetHashCode()、およびその効果でさえ保証されません。

5
supercat

開発者にとって、ポインターのconceptを理解すること、つまり、間接性を理解することは重要だと思います。それは必ずしもポインターを使用する必要があるという意味ではありません。参照のの概念は、ポインターのの概念と異なることを理解することも重要ですほぼ常に参照のポインターです。

つまり、参照を保持する変数は、オブジェクトへのポインターを保持するポインターサイズのメモリブロックにすぎません。ただし、この変数は、ポインター変数を使用できるのと同じ方法で使用することはできません。 C#(およびC、およびC++、...)では、ポインターは配列のようにインデックス付けできますが、参照はできません。 C#では、参照はガベージコレクターによって追跡されますが、ポインターは追跡できません。 C++では、ポインターを再割り当てできますが、参照は再割り当てできません。構文的にも意味的にも、ポインターと参照はまったく異なりますが、機械的には同じです。

4
P Daddy

ポインターは、アプリケーションのアドレス空間内の任意のバイトを指すことができます。参照は、.NET環境によって厳しく制限され、制御および管理されます。

4
jdigital

ポインターを多少複雑にするのは、ポインターが何であるかではなく、ポインターで何ができるかです。そして、ポインターへのポインターへのポインターがあるとき。それは本当に楽しみ始めたときです。

1
Robert C. Barth

ポインターに対する参照の最大の利点の1つは、シンプルさと読みやすさの向上です。いつものように、何かを単純化すると、使いやすくなりますが、柔軟性と制御が犠牲になりますが、低レベルのものは他の人が言及しているようになります。

ポインターはしばしば「ugい」と批判されます。

class* myClass = new class();

これを使用するたびに、まず次のいずれかの方法で逆参照する必要があります

myClass->Method() or (*myClass).Method()

読みやすさをいくらか失い、複雑さを増しているにもかかわらず、人々はポインターをパラメーターとして頻繁に使用する必要がありました。そのため、値で渡すのではなく、実際のオブジェクトを変更でき、巨大なオブジェクトをコピーする必要がないため、パフォーマンスが向上します。

私にとって、これが参照が最初に「生まれた」理由であり、ポインターと同じ利点を提供しますが、ポインター構文はすべてありません。これで、実際のオブジェクト(値だけでなく)を渡すことができ、オブジェクトとやり取りするより読みやすい通常の方法が得られます。

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

C++参照は、C#/ Java参照とは異なり、値を一度割り当てると、それを再割り当てできませんでした(宣言時に割り当てる必要があります)これは、constポインター(別のオブジェクトを指すことができないポインター)を使用するのと同じでした。

JavaとC#は非常に高水準の現代言語であり、長年にわたってC/C++に蓄積していた多くの混乱をクリーンアップし、ポインターは間違いなく「クリーンアップ」する必要のあるものの1つでした。

ポインタを知ることに関するあなたのコメントがあなたをより強力なプログラマにする限り、ほとんどの場合これは真実です。 「方法」を知っている場合、知らずに単に使用するのではなく、何かが機能するので、これはしばしばあなたにエッジを与えることができると言います。 Edgeの大きさは常に異なります。結局のところ、実装方法を知らずに何かを使用することは、OOPおよびInterfacesの多くの美しさの1つです。

この特定の例では、ポインターについて知っていると、参照に役立ちますか? C#参照はオブジェクトそのものではなく、オブジェクトを指すことを理解することは非常に重要な概念です。

#1:値渡しではありませんまず、ポインターを使用する場合、ポインターがアドレスのみを保持していることがわかります。変数自体はほとんど空であるため、引数として渡すのはとても良いことです。パフォーマンスの向上に加えて、実際のオブジェクトを操作しているため、変更は一時的ではありません

#2:Polymorphism/Interfacesインターフェイスタイプの参照があり、それがオブジェクトを指している場合、オブジェクトにもっと多くの機能がある場合でも、そのインターフェイスのメソッドのみを呼び出すことができます。オブジェクトは、同じメソッドを異なる方法で実装することもできます。

これらの概念をよく理解していれば、ポインターを使用していなかったからといって足りないということはないと思います。 C++は、プログラミングを学習するための言語としてよく使用されます。これは、時々手を汚すのが良いからです。また、低レベルの側面を扱うことで、現代言語の快適さを理解できます。私はC++で始め、現在はC#プログラマーであり、生のポインターを使用することで、内部で何が起こっているかをよりよく理解できるようになったと感じています。

すべての人がポインターから始める必要はないと思いますが、重要なのは、値型の代わりに参照が使用される理由を理解することであり、それを理解する最良の方法は、その祖先であるポインターを調べることです。

1
Despertar