私は最近C++の学習を始めたばかりであり、ほとんどの人が(私が読んでいるものによれば)ポインタに苦労しています。
伝統的な意味ではなく、それらが何であり、なぜ使用されているのか、そしてそれらがどのように役立つのかを理解していますが、ポインタのインクリメントがどのように役立つのか理解できません。有用な概念と慣用的なC++?
この質問は、Bjarne Stroustrupの本 A Tour of C++ を読み始めた後に出されました。私はJavaに精通しているため、この本を勧められました。良い「切り替え」本になります。
配列がある場合、配列の要素を指すようにポインタを設定できます。
int a[10];
int *p = &a[0];
ここでp
はa
の最初の要素、つまりa[0]
を指します。これで、次の要素を指すようにポインターを増分できます。
p++;
ここで、p
は2番目の要素a[1]
を指します。ここで*p
を使用して要素にアクセスできます。これは、Javaとは異なります。ここでは、配列の要素にアクセスするために整数のインデックス変数を使用する必要があります。
C++でポインタをインクリメントして、そのポインタが配列の要素を指すnotを指す場合はndefined behaviourです。
ポインターのセマンティクスは、C++標準ライブラリ(Alexander Stepanovの [〜#〜] stl [〜# 〜] )
ここでの重要な概念は、STLがコンテナー、アルゴリズム、イテレーターを中心に設計されていることです。ポインタは単にiteratorsです。
もちろん、ポインタをインクリメント(または加算/減算)する機能はCに戻ります。多くのC文字列操作アルゴリズムは、ポインタ演算を使用して簡単に記述できます。次のコードを検討してください。
char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));
このコードは、ポインタ演算を使用して、ヌルで終了するC文字列をコピーします。ループは、ヌルに遭遇すると自動的に終了します。
C++では、ポインターのセマンティクスはiteratorsの概念に一般化されています。ほとんどの標準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
関数は、(単純なポインターを含む)増分をサポートするanyイテレーターで機能するテンプレートです。プレーンなC文字列に対して同じcopy
関数を使用できます。
const char* s1 = "abcdef";
std::vector<char> buf;
std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));
イテレータをサポートするstd::map
またはstd::set
またはanyカスタムコンテナでcopy
を使用できます。
ポインタは特定のタイプのイテレータであることに注意してください:ランダムアクセスイテレータ、つまり、+
および-
演算子によるインクリメント、デクリメント、および前進をサポートします。他のイテレータタイプは、ポインタセマンティクスのサブセットのみをサポートします。双方向イテレータは、少なくともインクリメントとデクリメントをサポートします。 フォワードイテレータは少なくともインクリメントをサポートしています。 (すべての反復子タイプは逆参照をサポートしています。)copy
関数には、少なくとも増分をサポートする反復子が必要です。
イテレータのさまざまな概念について読むことができます ここ 。
したがって、ポインタのインクリメントは、C配列を反復する、またはC配列の要素/オフセットにアクセスするための慣用的なC++の方法です。
ポインター算術はCにあったのでC++にあります。ポインター算術はassemblerの通常のイディオムであるためCにあります。
「インクリメントレジスタ」の方が「定数値1をロードしてレジスタに追加する」よりも高速なシステムはたくさんあります。さらに、かなりの数のシステムで、単一の命令で「レジスタBで指定されたアドレスからDWORDをAにロードし、次にsizeof(DWORD)をBに追加」できます。最近では、最適化コンパイラがこれを整理することを期待しているかもしれませんが、これは1973年には実際の選択肢ではありませんでした。
これは、C配列の境界チェックが行われず、C文字列にサイズが埋め込まれていないのと同じ理由です。言語は、すべてのバイトとすべての命令がカウントされるシステムで開発されました。