まあ、私は基本的にポインタの使用方法を理解していますが、より良いプログラミングを行うためにそれらをどのように使用するのが最善かはわかりません。
ポインターを使用することで解決できる、良いプロジェクトや問題は何ですか?
まあ、私は基本的にポインタの使用方法を理解していますが、より良いプログラミングを行うためにそれらをどのように使用するのが最善かはわかりません。
ポインターを使用することで解決できる、良いプロジェクトや問題は何ですか?
回答:
メモリ内の大量のデータを操作することは、ポインタが本当に輝く場所です。
参照によって大きなオブジェクトを渡すことは、単純に古い数値を渡すことと同等です。オブジェクトをコピーして変更し、コピーを元の場所に戻すために戻すのではなく、必要な部分を直接操作できます。
ポインターの概念により、データのストレージを複製することなく、アドレスでデータを参照できます。このアプローチにより、次のような効率的なアルゴリズムを作成できます。
ソート
ソートアルゴリズムでデータを移動する場合、データ自体の代わりにポインターを移動できます。100文字の文字列で数百万行をソートすると考えてください。多くの不必要なデータ移動を保存します。
リンクリスト
レコードに関連付けられたデータ全体ではなく、次または前のアイテムの場所を保存できます。
パラメータの受け渡し
この場合、データ自体ではなくデータのアドレスを渡します。繰り返しますが、数百万行で実行される名前圧縮アルゴリズムを考えてください。
この概念は、ポインターが外部キーに似ているリレーショナルデータベースなどのデータ構造に拡張できます。一部の言語では、C#やCOBOLなどのポインターの使用を推奨していません。
例は、次のような多くの場所にあります。
次の投稿は何らかの形で関連している可能性があります。
ポインタを使用すると、要素が複雑な方法で他の複数の要素に関連付けられる可能性のある、不連続で非線形のデータ構造を作成できます。
リンクリスト(単一、二重、および循環リンク)、ツリー(赤黒、AVL、トライ、バイナリ、スペースパーティション分割…)、およびグラフはすべて、値だけでなく参照に関して最も自然に構築できる構造の例です。 。
1つの簡単な方法は、多態性です。ポリモーフィズムはポインターでのみ機能します。
また、動的メモリ割り当てが必要なときはいつでもポインタを使用します。Cでは、これは通常、データを配列に格納する必要があるが、コンパイル時にサイズがわからない場合に発生します。次に、mallocを呼び出して、メモリとそれにアクセスするためのポインタを割り当てます。また、知っているかどうかにかかわらず、配列を使用するときはポインターを使用しています。
for(int i = 0; i < size; i++)
std::cout << array[i];
と同等です
for(int i = 0; i < size; i++)
std::cout << *(array + i);
この知識により、配列全体を1行でコピーするなど、本当にクールなことができます。
while( (*array1++ = *array2++) != '\0')
C ++では、newを使用してオブジェクトにメモリを割り当て、ポインターに格納します。これは、コンパイル時ではなく実行時にオブジェクトを作成する必要があるときにいつでも行います(つまり、メソッドが新しいオブジェクトを作成してリストに保存します)。
ポインターをよりよく理解するには:
継承を使用するプロジェクトを見つけてください。
これが私が歯を切ったプロジェクトです。
ファイルから2つのnxn行列を読み取り、それらに対して基本的なベクトル空間操作を実行し、その結果を画面に出力します。
これを行うには、2つの配列の配列(多次元動的配列)があるため、動的配列を使用し、ポインターで配列を参照する必要があります。そのプロジェクトを終了すると、ポインターの使用方法がかなりわかります。
ポインターが重要である理由を本当に理解するには、ヒープ割り当てとスタック割り当ての違いを理解する必要があります。
以下は、スタック割り当ての例です。
struct Foo {
int bar, baz
};
void foo(void) {
struct Foo f;
}
スタックに割り当てられたオブジェクトは、現在の関数実行の間のみ存在します。への呼び出しfoo
が範囲外になると、変数も範囲外になりますf
。
これが問題になる1つのケースは、関数から整数型以外のものを返す必要がある場合です(たとえば、上記の例のFoo構造体)。
たとえば、次の関数は、いわゆる「未定義の動作」になります。
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
struct Foo f;
return &f;
}
struct Foo *
関数から本当に必要なものを返したい場合、ヒープ割り当てが必要です:
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
return malloc(sizeof(struct Foo));
}
この関数malloc
は、ヒープにオブジェクトを割り当て、そのオブジェクトへのポインターを返します。ここでは「オブジェクト」という用語が大まかに使用されていることに注意してください。これは、オブジェクト指向プログラミングの意味でのオブジェクトではなく、「何か」を意味します。
ヒープに割り当てられたオブジェクトの有効期間は、プログラマーによって制御されます。このオブジェクトのメモリは、プログラマが解放するまで、つまり呼び出しによって、free()
またはプログラムが終了するまで予約されます。
編集:この質問がC ++の質問としてタグ付けされていることに気づきませんでした。C ++演算子new
とnew[]
は、と同じ機能を実行しmalloc
ます。演算子delete
とdelete[]
はに類似していfree
ます。一方でnew
とdelete
、C ++オブジェクトを割り当て、無料のために排他的に使用を使用する必要があるmalloc
とfree
C ++のコードで完全に合法です。
Cで自明でないプロジェクトを書くと、ポインターを使用する方法とタイミングを把握する必要があります。C ++では、ポインターを内部的に管理するRAII対応オブジェクトを主に使用しますが、Cではrawポインターがより一般的な役割を持ちます。どのようなプロジェクトを行うべきかについては、些細なことではありません。
最後のものをお勧めします。
ポインターで解決できるほとんどすべてのプログラミングの問題は、他のより安全なタイプの参照(C ++参照ではなく、変数が他の場所に保存されているデータの値を参照するという一般的なCS概念)で解決できます。
メモリアドレスを直接操作できる参照の特定の低レベル実装であることによるポインタは非常に強力ですが、使用するのはわずかに危険です(たとえば、プログラム外のメモリの場所を指す)。
ポインターを直接使用する利点は、安全性チェックを行わなくてもよいため、ポインターがわずかに高速になることです。Cスタイルポインターを直接実装しないJavaなどの言語は、わずかなパフォーマンスヒットを被りますが、デバッグが困難な多くのタイプの状況を軽減します。
インダイレクションが必要な理由については、リストはかなり長いですが、本質的に2つの重要なアイデアは次のとおりです。
selected_object
オブジェクトの1つへの参照である変数を持つことは、現在のオブジェクトの値を新しい変数にコピーするよりもはるかに効率的です。ほとんどの場合、ピクセルレベルの画像操作は、ポインターを使用してより簡単かつ高速になります。場合によっては、ポインターのみを使用して可能です。
ポインターはCでのデータ構造の実装に不可欠な部分であり、データ構造は重要なプログラムの重要な部分です。
ポインターが非常に重要である理由を知りたい場合は、リンクリストとは何かを学習し、ポインターを使用せずにリストを作成することをお勧めします。私はあなたに不可能な挑戦を設定していません(ヒント:メモリ内の場所を参照するためにポインタが使用されていますが、配列内の物事をどのように参照しますか?)。
さまざまなStackExchangeサイトを熟読すると、このような質問をするのがかなり流行していることがわかります。批判と下票の危険があるので、正直に言う。これは、トロールや炎を意味するものではなく、質問に対して正直な評価をすることで、私が手助けすることを意味しているだけです。
そして、その評価は次のとおりです。これは、Cプログラマーに尋ねる非常に奇妙な質問です。「Cがわからない」というだけです。さらにシニカルに分析すると、この「質問」へのフォローアップの裏声があります。独立した研究のために?」誰かができる「答え」は、根底にある概念とその使用法を理解するために足を踏み入れて行うことに代わるものではありません。
Webに行ってこれを人々に尋ねるよりも、Cを直接よく学ぶ方が建設的だと思います。Cを熟知していれば、このような質問をする必要はありません。「歯ブラシを使用することでどのような問題を解決するのが最善ですか?」
大きなメモリオブジェクトを操作しているときにポインタは本当に輝いていますが、ポインタなしでも同じことを行う方法があります。
ポインターは、いわゆる動的プログラミングに不可欠です。プログラムを実行する前に必要なメモリー量がわからない場合です。動的プログラミングでは、実行時にメモリチャンクを要求し、それらに独自のデータを配置できます。そのため、これらのデータチャンクを操作するにはポインターまたは参照(ここでは違いは重要ではありません)が必要です。
実行時に特定のメモリを要求し、新しく取得したメモリにデータを配置できる限り、次のことができます。
自己拡張データ構造を持つことができます。これらは、容量が足りない限り、追加のメモリを要求することで拡張できる構造です。すべての自己拡張構造の重要な特性は、小さなメモリブロック(構造に応じてノード、リスト項目などと呼ばれる)で構成され、すべてのブロックに他のブロックへの参照が含まれることです。この「リンクされた」構造は、グラフ、ツリー、リストなど、最新のデータ型の大部分を構築します。
OOP(オブジェクト指向プログラミング)パラダイムを使用してプログラミングできます。OOP全体は、直接変数ではなく、クラスインスタンス(オブジェクトと呼ばれる)への参照を使用して操作することに基づいています。ポインターなしでは単一のインスタンスは存在できません(ポインターなしでも静的専用クラスを使用することは可能ですが、それはむしろ例外です)。
おかしい、私はちょうどC ++に関する質問に答えて、ポインターについて話しました。
ショートバージョンでは、1)使用しているライブラリが強制的に2)NULL可能な参照を必要としない限り、ポインターは必要ありません。
配列、リスト、文字列などが必要な場合は、スタック上に配置して、stlオブジェクトを使用します。stlオブジェクトを返すか渡すには、オブジェクトの代わりにポインターをコピーする内部コードがあり、データを書き込む場合にのみデータをコピーするため、高速(未確認の事実)です。これは通常のC ++であり、ライブラリ作成者にとって簡単になる新しいC ++ 11でもありません。
ポインターを使用する場合は、これらの2つの条件のいずれかを確認してください。1)nullを許可する可能性のある入力を渡している。例はオプションのファイル名です。2)所有権を譲りたい場合。ポインタを渡したり返したりする場合と同様に、コピーのコピーが残っていないか、配ったポインタを使用していない
ptr=blah; func(ptr); //never use ptr again for here on out.
しかし、私は非常に長い間、ポインターやスマートポインターを使用していなかったため、アプリケーションのプロファイルを作成しました。非常に高速に実行されます。
追加のメモ:独自の構造体を作成し、それらを渡していることに気付きました。ポインターを使用せずにこれを行うにはどうすればよいですか?そのSTLコンテナではないので、refによる受け渡しは遅いです。私は常に自分のデータリスト/デック/マップなどをロードします。何らかのリスト/マップでない限り、オブジェクトを返すことを覚えていません。文字列でさえありません。私は単一のオブジェクトのコードを見ましたが、私はこの{ MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }
ようなことをしているので、ほとんどオブジェクトを返す(複数)か、参照を事前に割り当て/渡す(単一)にします。
これで、自分がdequeやmapなどを書いている場合は、ポインターを使用する必要があります。しかし、あなたはする必要はありません。STLに任せて、恐らくそれを後押ししてください。データとソリューションを作成するだけです。それらを保持する容器ではありません;)
ポインタを使用しないでください:D。あなたに強制するライブラリを扱う幸運
あなたの質問にはC ++というタグが付けられているので、その言語の質問に答えます。
C ++では、ポインターと参照に区別があるため、特定の動作を容易にするためにポインター(またはスマートポインター)が必要な2つのシナリオがあります。他の状況でも使用できますが、「それらを使用するのに最適な方法」を尋ねます。他のすべての状況では、より良い代替手段があります。
基本クラスポインターを使用すると、ポインターが指すオブジェクトの種類に依存する仮想メソッドを呼び出すことができます。
オブジェクトを動的に(スタックではなくヒープ上に)作成する場合、ポインターが必要です。これは、オブジェクトのライフタイムを、それが作成されたスコープよりも長くしたい場合に必要です。
「良いプロジェクトや解決すべき問題」に関しては、他の人がすでにここで言っているように、どんな些細なプロジェクトでもポインタを利用します。