私は最近C ++の学習を始めましたが、ほとんどの人が(これまで読んでいたことによると)ポインターに苦労しています。
伝統的な意味ではなく、私はそれらが何であり、なぜ使用されているのか、どのように役立つのかを理解していますが、ポインタのインクリメントがどのように役立つかを理解することはできません、誰もポインタのインクリメントが便利な概念と慣用的なC ++?
この質問は、Bjarne StroustrupによるA Tour of C ++の本を読み始めた後に出てきたものです。 。
私は最近C ++の学習を始めましたが、ほとんどの人が(これまで読んでいたことによると)ポインターに苦労しています。
伝統的な意味ではなく、私はそれらが何であり、なぜ使用されているのか、どのように役立つのかを理解していますが、ポインタのインクリメントがどのように役立つかを理解することはできません、誰もポインタのインクリメントが便利な概念と慣用的なC ++?
この質問は、Bjarne StroustrupによるA Tour of C ++の本を読み始めた後に出てきたものです。 。
回答:
配列がある場合、配列の要素を指すポインターを設定できます。
int a[10];
int *p = &a[0];
ここでは、p
の最初の要素を指しa
ています、a[0]
。これで、次の要素を指すようにポインターを増分できます。
p++;
次にp
、2番目の要素を指しa[1]
ます。ここで要素にアクセスできます*p
。これは、整数のインデックス変数を使用して配列の要素にアクセスする必要があるJavaとは異なります。
ポインタが配列の要素を指していない C ++でのポインタのインクリメントは、未定義の動作です。
ポインタのセマンティクスが(アレクサンダー・ステパノフさんのオフに基づいてC ++標準ライブラリの背後にある設計哲学の基本的な側面を反映するので、ポインタをインクリメントすること、慣用C ++でSTLを)
ここで重要な概念は、STLがコンテナ、アルゴリズム、およびイテレータを中心に設計されていることです。ポインタは単なる反復子です。
もちろん、ポインタをインクリメント(または加算/減算)する機能はCに戻ります。多くのC文字列操作アルゴリズムは、ポインタ演算を使用して簡単に記述できます。次のコードを検討してください。
char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));
このコードは、ポインター演算を使用して、ヌル終了Cストリングをコピーします。ループは、ヌルが検出されると自動的に終了します。
C ++では、ポインターのセマンティクスはイテレーターの概念に一般化されます。ほとんどの標準C ++コンテナには、イテレータが用意されています。イテレータには、begin
およびend
メンバー関数を介してアクセスできます。イテレータは、インクリメント、デリファレンス、デクリメントまたはアドバンスされることがあるという点で、ポインタのように動作します。
を反復処理するにはstd::string
、次のように言います。
std::string s = "abcdef";
std::string::iterator it = s.begin();
for (; it != s.end(); ++it) std::cout << *it;
プレーンなC文字列へのポインタをインクリメントするのと同じように、イテレータをインクリメントします。このコンセプトが強力な理由は、テンプレートを使用して、必要なコンセプト要件を満たすあらゆるタイプのイテレータで機能する関数を作成できるためです。そして、これがSTLの力です。
std::string s1 = "abcdef";
std::vector<char> buf;
std::copy(s1.begin(), s1.end(), std::back_inserter(buf));
このコードは、文字列をベクターにコピーします。このcopy
関数は、インクリメントをサポートするイテレータ(プレーンポインタを含む)で動作するテンプレートです。copy
プレーンなC文字列で同じ関数を使用できます。
const char* s1 = "abcdef";
std::vector<char> buf;
std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));
私たちは、使用することができますcopy
上std::map
かstd::set
または任意のカスタム・コンテナサポート反復子います。
ポインタは特定の種類のイテレータであることに注意してください:ランダムアクセスイテレータは、+
and -
演算子によるインクリメント、デクリメント、および前進をサポートすることを意味します。他のイテレータタイプは、ポインタセマンティクスのサブセットのみをサポートします。双方向イテレータは、少なくともインクリメントとデクリメントをサポートします。前方イテレータ支持体は、少なくともインクリメント。(すべてのイテレータタイプは逆参照をサポートしています。)このcopy
関数には、少なくともインクリメントをサポートするイテレータが必要です。
イテレータのさまざまな概念については、こちらをご覧ください。
したがって、ポインタの増分は、C配列を反復処理する、またはC配列の要素/オフセットにアクセスするための慣用的なC ++の方法です。
ポインター演算はCにあったため、C ++にあります。ポインター演算は、アセンブラーの通常のイディオムであるため、Cにあります。
「増分レジスタ」が「定数値1をロードしてレジスタに追加する」よりも速いシステムがたくさんあります。さらに、1つの命令で「レジスタBで指定されたアドレスからDWORDをAにロードしてから、sizeof(DWORD)をBに追加する」ことができるシステムがかなりあります。最近では、最適化コンパイラがこれを整理してくれると期待しているかもしれませんが、1973年にはこれは実際には選択肢ではありませんでした。
これは、C配列が境界チェックされておらず、C文字列にサイズが埋め込まれていないという基本的に同じ理由です。言語は、すべてのバイトとすべての命令がカウントされるシステムで開発されました。