私は C と C++ が大好きですが、ヌル終了文字列の選択には首をかしげずにはいられません。
- 長さプレフィックス付き(つまりPascal)文字列はCより前に存在していた
- 長さのプレフィックスが付いた文字列を使用すると、一定時間で長さを検索できるため、いくつかのアルゴリズムが高速化されます。
- 長さプレフィックス付き文字列により、バッファ オーバーラン エラーが発生しにくくなります。
- 32 ビット マシンでも、文字列を使用可能なメモリのサイズにすると、長さプレフィックス付き文字列は、ヌル終端文字列より 3 バイトだけ広くなります。16 ビット マシンでは、これは 1 バイトです。64 ビット マシンでは、4 GB が妥当な文字列長制限ですが、マシン ワードのサイズまで拡張する場合でも、64 ビット マシンには通常十分なメモリがあるため、追加の 7 バイトはヌル引数のようになります。元の C 標準は (メモリの点で) 非常に貧弱なマシン向けに作成されたことは知っていますが、ここでは効率の議論は納得できません。
- 他のほぼすべての言語 (Perl、Pascal、Python、Java、C# など) は、長さプレフィックス付きの文字列を使用します。これらの言語は、文字列の操作効率が高いため、文字列操作のベンチマークでは通常 C より優れています。
- C++ ではテンプレートを使用してこれを少し修正しました
std::basic_string
が、ヌル終了文字列を期待する単純な文字配列は依然として広く使用されています。これもヒープ割り当てを必要とするため不完全です。 - ヌルで終了する文字列では、文字列内に存在できない文字 (つまり、ヌル) を予約する必要がありますが、長さプレフィックス付きの文字列には埋め込まれたヌルを含めることができます。
これらのうちいくつかは C よりも最近になって明らかになったため、C がこれらを知らなかったとしても不思議ではありません。しかし、いくつかは C が登場するずっと前から明らかでした。明らかに優れている長さのプレフィックスの代わりに、なぜヌル終端文字列が選択されたのでしょうか。
編集: 上記の効率性に関する私のポイントについて、事実を求める人がいました(そして、私がすでに提供した事実が気に入らなかったのです)。その理由は、次のようないくつかの点にあります。
- ヌル終端文字列を使用した連結には、O(n + m) の時間計算量が必要です。長さのプレフィックス付けには、多くの場合、O(m) のみが必要です。
- ヌル終端文字列を使用した長さの計算には O(n) の時間計算量が必要です。長さのプレフィックス付けは O(1) です。
- 長さと連結は、最も一般的な文字列操作です。ヌルで終了する文字列の方が効率的になるケースはいくつかありますが、その頻度ははるかに低くなります。
以下の回答から、ヌル終了文字列の方が効率的なケースをいくつか示します。
- 文字列の先頭を切り取って、それを何らかのメソッドに渡す必要がある場合。長さプレフィックスはおそらくアライメント ルールに従う必要があるため、元の文字列を破棄できる場合でも、長さプレフィックスを使用してこれを定数時間で実行することはできません。
- 文字列を文字ごとにループするだけの場合には、CPU レジスタを節約できる場合があります。ただし、これは文字列を動的に割り当てていない場合にのみ機能することに注意してください (動的に割り当てた場合は文字列を解放する必要があり、保存した CPU レジスタを使用して、malloc などから最初に取得したポインタを保持する必要があります)。
上記のどれも、length や concat ほど一般的ではありません。
以下の回答でもう 1 つ主張されています。
- 紐の端を切る必要があります
しかし、これは誤りです。ヌル終了文字列と長さプレフィックス付き文字列にかかる時間は同じです。(ヌル終了文字列は、新しい終了位置にヌルを挿入するだけであり、長さプレフィックス付き文字列はプレフィックスから減算するだけです。)
ベストアンサー1
から馬の口
BCPL、B、C のいずれも、言語内で文字データを強力にサポートしていません。それぞれが文字列を整数のベクトルのように扱い、いくつかの規則によって一般的なルールを補足しています。BCPL と B の両方で、文字列リテラルは、セルにパックされた文字列の文字で初期化された静的領域のアドレスを示します。BCPL では、パックされた最初のバイトに文字列の文字数が含まれます。B ではカウントはなく、文字列は特殊文字 (B では と表記) で終了します
*e
。この変更は、カウントを 8 ビットまたは 9 ビットのスロットに保持することによって生じる文字列の長さの制限を回避するためと、カウントを維持することは、経験上、ターミネータを使用するよりも不便であるように思われたために行われました。
デニス・M・リッチー、C言語の開発