ポインターを使用して解決するのに最適なプログラミング問題は何ですか?[閉まっている]


58

まあ、私は基本的にポインタの使用方法を理解していますが、より良いプログラミングを行うためにそれらをどのように使用するのが最善かはわかりません。

ポインターを使用することで解決できる、良いプロジェクトや問題は何ですか?


どのプロジェクト/プログラムではなく、ポインターを使用してどのプログラミングの問題を解決するのが最適かを尋ねます。ポインターを使用してプログラムを作成することも、そうでないこともできます-それら(プロジェクト)は、意味のある答えを得るには大きすぎる構造です。
オデッド

37
プログラミングの問題は、解決するのではなく、ポインターを使用して作成されます。:)
xpda

4
実際、ポインターなしでどの問題を解決できるのでしょうか。確かに非常に少ない。
deadalnix

4
@deadalnix、明示的なポインターを意味するか、暗黙的なポインターを意味するかによって異なります。暗黙的なポインター(つまり、どこかにポインターが使用されている)を意味する場合、スタックまたはヒープを使用するプログラムを作成することは不可能です。明示的なポインターを意味する場合、制約はありません。コンパイラーによって最適化されていると仮定して、新しいスタックフレームを作成することにより(基本的に関数呼び出しを使用)、テール呼び出しを使用して割り当て解除を行うことにより、任意のサイズの割り当てを行うことができます。
dan_waterworth

3
うーん、これらの答えのいくつかはひどいです。

回答:


68

メモリ内の大量のデータを操作することは、ポインタが本当に輝く場所です。

参照によって大きなオブジェクトを渡すことは、単純に古い数値を渡すことと同等です。オブジェクトをコピーして変更し、コピーを元の場所に戻すために戻すのではなく、必要な部分を直接操作できます。


12
これはすばらしい答えであり、大量のデータを操作するために大きなオブジェクトを必要としないことを付け加えます。時折大きなオブジェクトを操作することでそれを行うことができますが、さらに一般的な問題は、非常に頻繁に小さなオブジェクトの束を操作することです。考えてみれば、これはすべてのコンピュータープログラムに関するものです。

44

ポインターの概念により、データのストレージを複製することなく、アドレスでデータを参照できます。このアプローチにより、次のような効率的なアルゴリズムを作成できます。

  1. ソート
    ソートアルゴリズムでデータを移動する場合、データ自体の代わりにポインターを移動できます。100文字の文字列で数百万行をソートすると考えてください。多くの不必要なデータ移動を保存します。

  2. リンクリスト
    レコードに関連付けられたデータ全体ではなく、次または前のアイテムの場所を保存できます

  3. パラメータの受け渡し
    この場合、データ自体ではなくデータのアドレスを渡します。繰り返しますが、数百万行で実行される名前圧縮アルゴリズムを考えてください。

この概念は、ポインターが外部キーに似ているリレーショナルデータベースなどのデータ構造に拡張できます。一部の言語では、C#やCOBOLなどのポインターの使用を推奨していません。

例は、次のような多くの場所にあります。

次の投稿は何らかの形で関連している可能性があります。


14
私は、C#がポインターの使用を奨励していないとは言いません。代わりに、アンマネージポインターの使用を推奨していません。C#には、値ではなく参照によって渡されるものがたくさんあります。
ブラコメン

良いもの、良い説明
サビー

5
C#のコメントに投票する必要がありました。C#の「参照」はポインターと同等です。C#は、マネージヒープ内のオブジェクトへのポインターを実際に処理しているだけであるという認識からあなたを引き離します。
MattDavey

1
@MattDavey:C#参照はポインターではありません。それらから追加したり、配列にインデックスを付けたり、複数のインダイレクションを使用したりすることはできません。たとえば、参照への参照を持つことはできません。
ビリーONeal

5
@Billy ON「それで算術ができるのは単なるポインターだ」という考えは率直に言って滑luですが、私はあなたと議論する忍耐を持っていません。
MattDavey

29

ポインタを使用すると、要素が複雑な方法で他の複数の要素に関連付けられる可能性のある、不連続で非線形のデータ構造を作成できます。

リンクリスト(単一、二重、および循環リンク)、ツリー(赤黒、AVL、トライ、バイナリ、スペースパーティション分割…)、およびグラフはすべて、値だけでなく参照に関して最も自然に構築できる構造の例です。 。


XORリンクリストを忘れている[=
dan_waterworth

@dan_waterworth:いいえ。黒魔術をよく知っています。
ジョンパーディ

8

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を使用してオブジェクトにメモリを割り当て、ポインターに格納します。これは、コンパイル時ではなく実行時にオブジェクトを作成する必要があるときにいつでも行います(つまり、メソッドが新しいオブジェクトを作成してリストに保存します)。

ポインターをよりよく理解するには:

  1. 従来のC文字列と配列をいじるプロジェクトを見つけてください。
  2. 継承を使用するプロジェクトを見つけてください。

  3. これが私が歯を切ったプロジェクトです。

ファイルから2つのnxn行列を読み取り、それらに対して基本的なベクトル空間操作を実行し、その結果を画面に出力します。

これを行うには、2つの配列の配列(多次元動的配列)があるため、動的配列を使用し、ポインターで配列を参照する必要があります。そのプロジェクトを終了すると、ポインターの使用方法がかなりわかります。


2
「ポリモーフィズムはポインターでのみ機能します。」正確ではありません:ポリモーフィズムは参照でも機能します。最終的にはポインタですが、特に初心者にとっては区別が重要だと思います。
ローランピレン

1行で配列をコピーする例は、実際には、一般的な配列ではなく、ゼロで終了する文字列を1行でコピーするためのものです。
-tcrosley

8

ポインターが重要である理由を本当に理解するには、ヒープ割り当てとスタック割り当ての違いを理解する必要があります。

以下は、スタック割り当ての例です。

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 ++演算子newnew[]は、と同じ機能を実行しmallocます。演算子deletedelete[]はに類似していfreeます。一方でnewdelete、C ++オブジェクトを割り当て、無料のために排他的に使用を使用する必要があるmallocfreeC ++のコードで完全に合法です。


1
(+1)「静的に割り当てられた変数」および「動的に割り当てられた変数」とも呼ばれます
;

4

Cで自明でないプロジェクトを書くと、ポインターを使用する方法とタイミングを把握する必要があります。C ++では、ポインターを内部的に管理するRAII対応オブジェクトを主に使用しますが、Cではrawポインターがより一般的な役割を持ちます。どのようなプロジェクトを行うべきかについては、些細なことではありません。

  • 小さくてシンプルなWebサーバー
  • Unixコマンドラインツール(less、cat、sortなど)
  • 学習のためだけでなく、それ自体のためにやりたい実際のプロジェクト

最後のものをお勧めします。


だから私はやりたいプロジェクトに行きます(2Dゲームのように)、それは私がポインターを必要とし、学ぶことになると思われますか?
ディソコ

3
私の経験では、スキルの面でやや手の届かない実際のプロジェクトを行うことが、学習するための最良の方法です。概念は、小さな「おもちゃ」プログラムを読み書きすることで学習できます。ただし、これらの概念を使用してより良いプログラムを作成する方法を実際に学習するには、単におもちゃ以外のプログラムを作成する必要があります。
ヴィクトールダール

3

ポインターで解決できるほとんどすべてのプログラミングの問題は、他のより安全なタイプの参照C ++参照ではなく、変数が他の場所に保存されているデータの値を参照するという一般的なCS概念)で解決できます。

メモリアドレスを直接操作できる参照の特定の低レベル実装であることによるポインタは非常に強力ですが、使用するのはわずかに危険です(たとえば、プログラム外のメモリの場所を指す)。

ポインターを直接使用する利点は、安全性チェックを行わなくてもよいため、ポインターがわずかに高速になることです。Cスタイルポインターを直接実装しないJavaなどの言語は、わずかなパフォーマンスヒットを被りますが、デバッグが困難な多くのタイプの状況を軽減します。

インダイレクションが必要な理由については、リストはかなり長いですが、本質的に2つの重要なアイデアは次のとおりです。

  1. 大きなオブジェクトの値のコピーは遅く、オブジェクトをRAMに2回保存します(非常にコストがかかる可能性があります)が、参照によるコピーは、(アドレス用に)数バイトのRAMを使用してほぼ瞬時に行われます。たとえば、メモリ内に〜1000個の大きなオブジェクト(それぞれ約1MBのRAM)があり、ユーザーが現在のオブジェクト(ユーザーによって処理される)を選択できる必要があるとします。selected_objectオブジェクトの1つへの参照である変数を持つことは、現在のオブジェクトの値を新しい変数にコピーするよりもはるかに効率的です。
  2. リンクリストやツリーなどの他のオブジェクトを参照する複雑なデータ構造を持ち、データ構造内の各アイテムがデータ構造内の他のアイテムを参照している。データ構造内の他のアイテムを参照することの利点は、リストの中央に新しいアイテムを挿入しただけで、リスト内のすべてのアイテムをメモリ内で移動する必要がないことを意味します(一定時間の挿入が可能)。

2

ほとんどの場合、ピクセルレベルの画像操作は、ポインターを使用してより簡単かつ高速になります。場合によっては、ポインターのみを使用して可能です。


1
「ポインタを使用しないとできない場合がある」という記述は真実ではないと思います。開発者にポインタを公開しない言語もありますが、同じ言語の問題を解決するために使用できます。
トーマスオーエンズ

おそらく...利用可能なすべての言語の知識はありませんが、ポインターを持たない言語を使用して画像に畳み込みフィルターを適用するルーチンを作成したくはありません。
デイブいや

@Thomas、あなたが正しい間、質問は開発者にポインターをさらすc ++についてです。
ジョナサンヘンソン

@Jonathanそれでも、ポインターを公開しない言語で問題を解決できる場合は、ポインターを使用せずにポインターを公開する言語でその問題を解決することもできます。これにより、最初の文は真になりますが、2番目の文は偽になります。(私の知る限り)ポインターを使用しないと解決できない問題はありません。
トーマスオーエンズ

1
それどころか、画像処理には「座標演算」が含まれます。たとえば、角度のサインとコサインを取得し、x座標とy座標で乗算します。その他の場合、ラップアラウンドまたは境界外のピクセル複製が必要です。この意味で、ポインター演算はかなり不十分です。
rwong

1

ポインターは、ユーザーに気付かれることなく、表面下の非常に多くのプログラミング言語で使用されています。C / C ++は単にそれらへのアクセスを提供します。

それらを使用する場合:データのコピーは非効率的であるため、可能な限り頻繁に。使用しない場合:個別に変更できる2つのコピーが必要な場合。(基本的にobject_1の内容をメモリ内の別の場所にコピーし、ポインターを返すことになります-今回はobject_2を指します)


1

ポインターはCでのデータ構造の実装に不可欠な部分であり、データ構造は重要なプログラムの重要な部分です。

ポインターが非常に重要である理由を知りたい場合は、リンクリストとは何かを学習し、ポインターを使用せずにリストを作成することをお勧めします。私はあなたに不可能な挑戦を設定していません(ヒント:メモリ内の場所を参照するためにポインタが使用されていますが、配列内の物事をどのように参照しますか?)。


データ構造に言及するための+1、アルゴリズムでの使用は当然の結果です。
マチューM.

0

実際の例として、指値注文書を作成します。

たとえば、ITCH 4.1フィードには、注文を「置換」する概念があり、価格(および優先順位)を変更できます。注文を取り出して、他の場所に移動できるようにしたい。ポインタを使用して両端キューを実装すると、操作が非常に簡単になります。


0

さまざまなStackExchangeサイトを熟読すると、このような質問をするのがかなり流行していることがわかります。批判と下票の危険があるので、正直に言う。これは、トロールや炎を意味するものではなく、質問に対して正直な評価をすることで、私が手助けすることを意味しているだけです。

そして、その評価は次のとおりです。これは、Cプログラマーに尋ねる非常に奇妙な質問です。「Cがわからない」というだけです。さらにシニカルに分析すると、この「質問」へのフォローアップの裏声があります。独立した研究のために?」誰かができる「答え」は、根底にある概念とその使用法を理解するために足を踏み入れて行うことに代わるものではありません。

Webに行ってこれを人々に尋ねるよりも、Cを直接よく学ぶ方が建設的だと思います。Cを熟知していれば、このような質問をする必要はありません。「歯ブラシを使用することでどのような問題を解決するのが最善ですか?」


0

大きなメモリオブジェクトを操作しているときにポインタは本当に輝いていますが、ポインタなしでも同じことを行う方法があります。

ポインターは、いわゆる動的プログラミングに不可欠です。プログラムを実行する前に必要なメモリー量がわからない場合です。動的プログラミングでは、実行時にメモリチャンクを要求し、それらに独自のデータを配置できます。そのため、これらのデータチャンクを操作するにはポインターまたは参照(ここでは違いは重要ではありません)が必要です。

実行時に特定のメモリを要求し、新しく取得したメモリにデータを配置できる限り、次のことができます。

  1. 自己拡張データ構造を持つことができます。これらは、容量が足りない限り、追加のメモリを要求することで拡張できる構造です。すべての自己拡張構造の重要な特性は、小さなメモリブロック(構造に応じてノード、リスト項目などと呼ばれる)で構成され、すべてのブロックに他のブロックへの参照が含まれることです。この「リンクされた」構造は、グラフ、ツリー、リストなど、最新のデータ型の大部分を構築します。

  2. OOP(オブジェクト指向プログラミング)パラダイムを使用してプログラミングできます。OOP全体は、直接変数ではなく、クラスインスタンス(オブジェクトと呼ばれる)への参照を使用して操作することに基づいています。ポインターなしでは単一のインスタンスは存在できません(ポインターなしでも静的専用クラスを使用することは可能ですが、それはむしろ例外です)。


0

おかしい、私はちょうど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。あなたに強制するライブラリを扱う幸運


0

ポインターは、メモリマップデバイスを操作するのに非常に役立ちます。(たとえば)制御レジスタを反映する構造を定義し、メモリ内の実際の制御レジスタのアドレスに割り当てて、直接操作することができます。MMUがシステムメモリ空間にマップした場合、カードまたはチップ上の転送バッファを直接指すこともできます。


0

私はポインターを人差し指と見なし、いくつかのことを行います。

  1. 誰かがあなたに持ち運びできないものを求めてきたとき、たとえば通りがどこにあるかなど、私はこの通りを「指し示す」でしょう。
  2. 何かを数えたり横断したりするときは、同じ指を使用します。それが配列で行われることです

この悪い答えを許してください


0

あなたの質問にはC ++というタグが付けられているので、その言語の質問に答えます。

C ++では、ポインターと参照に区別があるため、特定の動作を容易にするためにポインター(またはスマートポインター)が必要な2つのシナリオがあります。他の状況でも使用できますが、「それらを使用するのに最適な方法」を尋ねます。他のすべての状況では、より良い代替手段があります。

1.ポリモーフィズム

基本クラスポインターを使用すると、ポインターが指すオブジェクトの種類に依存する仮想メソッドを呼び出すことができます。

2.永続オブジェクトの作成

オブジェクトを動的に(スタックではなくヒープ上に)作成する場合、ポインターが必要です。これは、オブジェクトのライフタイムを、それが作成されたスコープよりも長くしたい場合に必要です。

「良いプロジェクトや解決すべき問題」に関しては、他の人がすでにここで言っているように、どんな些細なプロジェクトでもポインタを利用します。


誰かがポインタを使用するよりもオブジェクトをスライスすることを好むかもしれません。ハッピーデバッグ:D
deadalnix
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.