2

C で文字列を処理するには、単純なクラスベースの言語が必要ですが、代わりに便利な文字列ライブラリを作成しようとしています。Rstring私の考えは、内部const char* sandを持つ (「堅牢int lengthな文字列」)rstring_concatと呼ばれる構造体を使用して、不変の文字列を使用することです。rstring_substringRstringmalloc

ライブラリの最初のドラフトを書き上げて、char *. ただし、新しく割り当てられたポインターを返すことは、デストラクタのない PITA のようなものであることに気付きました。連結や部分文字列などの操作が行われるたびに、新しく割り当てられたメモリがいくつかあり、以前に持っていた文字列がぶらぶらしていて、おそらく役に立たないので、それらをfree'd にする必要があり、C にはデストラクタがありません、そのため、ユーザーはすべてを手動で移動する必要がありfreeます。

したがって、私の質問は、解放するための手動呼び出しを大量に行う必要を回避する賢い方法はありますか? おそらく、たとえば、小さな文字列のように振る舞う文字列を持つために、内部インデックスstartとインデックスを持つことはできますが、実際にはもっと多くの文字列が含まれていますか? endこれを行うための一般的に受け入れられている方法があるかどうか、または単に C の退屈なメモリ管理に行き詰まっているだけなのかどうかはわかりません。

おそらく最良の方法ですが、C で便利な文字列操作を行うために広く使用されているライブラリはありますか?

4

5 に答える 5

3

CI 用のより優れた文字列ライブラリが必要な場合は、The Better String Libraryをお勧めします。


C には、メモリ管理を簡素化する方法がありません。malloc を使用して割り当てたメモリは解放する必要があります。1 つの関数で多くの文字列を操作している場合は、文字registry列を登録する特別な場所を使用できます。レジストリは、登録されたすべての文字列を破棄する可能性があります。

例(インターフェースのみ、実装なし)

void rstring_reg_init(rstring_reg*);
void rstring_reg_destroy(rstring_reg*);
rstring rstring_reg_create(rstring_reg*, const char*);
void rstring_reg_register(rstring_reg*, rstring);
void rstring_reg_detach(rstring_reg*, rstring);

文字列が変更可能な場合は、レジストリを使用して文字列を作成することもできます (その場合はプールと呼びます)。文字列がそのプールを覚えていれば、作成時に自分自身を登録させることさえできます。これは、次のようなかなり「美しいコード」につながる可能性があります。

rstring f() {
    rstring_reg reg;
    rstring_reg_init(&reg);
    rstring a = rstring_reg_create(reg, "foo");
    rstring b = rstring_reg_create(reg, "bar");
    rstring ab = rstring_concat(a, b);
    rstring s = rstring_substr(ab, 1, 4);
    rstring_detach(s);
    rstring_reg_destroy(&reg);
    return s;
}

このコードが行うことは次のとおりです。

  • レジストリの作成
  • 両方ともレジストリを認識している文字列を作成aしますb
  • 新しいab文字列を作成します。レジストリに自動的に追加されます。
  • 新しいs文字列を作成します。レジストリにも追加されます。
  • sレジストリを返却したいので、レジストリから切り離します。
  • レジストリを破棄します。これにより、 が自動的に破棄されabab
  • Return s- f の呼び出し元は、そのメモリを管理する責任があります

最終的には、そのような獣を使用するよりも C++ を使用することをお勧めします。

本当に必要なのはRAIIであり、これは C++ または独自の GCC 拡張機能を使用してのみ可能です。

于 2013-02-03T01:54:36.607 に答える
0

Gstring型を含む多くの有用なデータ構造とサービスを備えたGlibを​​ご覧になることをお勧めします。その結果を直接Gstringに入れるものがあります。とても便利な。printf

本質的に、あなたの質問は文字列自体よりもメモリ割り当てに関するものです。文字列などのメモリ割り当てについては、Cからほとんど助けが得られません。一般的なアプローチはいくつかあります。

  1. 手動: すべてのオブジェクトに対してmalloc()およびfree()を呼び出します。
  2. 保守的なガベージコレクション: ベームコレクターは非常に成熟しています。いくつか問題はありますが、現時点ではよく理解されています。特定の風変わりなコーディング方法や積極的な最適化は避ける必要があります。
  3. ガベージコレクションをカウントする参照:これには、文字列への各ポインタの作成および破棄時に、文字列内のカウンタをインクリメント/デクリメントするルーチン を呼び出す必要がreferenceあります。dereferenceデクリメントによってカウントがゼロになると、文字列が解放されます。これは、malloc()やfree()と同じように面倒でエラーが発生しやすいですが、文字列の存続期間がいくつかの個別のコードチャンクのいずれかで終わる可能性がある複雑なケースを処理できます。
  4. プールの割り当て。 このスキームでは、任意の数の割り当てプールを作成します。文字列を作成するときは、文字列を割り当てるプールを指定します。プールは一度に解放されます。
  5. スタック割り当て。 文字列は、マーク操作とリリース操作がある単一のメモリプールから割り当てられます。Releaseを呼び出すと、すべての文字列が解放され、Markが最後に呼び出されたポイントに戻ります。

上記のすべての組み合わせが一般的です。たとえば、GNOUbstacksは4と5を組み合わせます。

于 2013-02-03T03:37:17.603 に答える
0

少なくとも私が理解しているように、不変の文字列の要点は、ストレージを共有することでコピーを回避できることです。(それが重要な場合は、いくつかのロックの問題を回避することもできます。) しかし、その場合、参照カウントを維持するように強制しない限り、クライアントが文字列を解放することを実際に許可することはできません。これは本当に面倒です。

ただし、多くのアプリケーションでは、Apache ランタイム (APR) と同じようにメモリ プールを使用できます。メモリ プールで作成されたオブジェクトは、個別には解放されません。プール全体が解放されます。これにはライフタイムの分析が必要であり、コンパイラが簿記を行わないため、あいまいなバグにつながる可能性がありますが、一般的に参照カウントよりも作業が少なく、割り当てと解放の両方が非常に高速です。これは、ストレージの解放がやや不正確であるという他の欠点を相殺する可能性があります。

メモリ プール アーキテクチャは、サーバー (および特定の種類の UI アプリケーション) に典型的な、ある種の要求ベースの制御フローがある場合に最適に機能します。リクエストベースの構造では、リクエストごとにメモリ プールが初期化され、リクエスト処理が終了すると解放されます。リクエストがサーバーの状態に永続的な変更を加える場合、一部のデータを一時プールから (より) 永続的なプールに移動する必要がある場合がありますが、そのような変更は比較的まれです。ステージに適切に分割できる要求 (一時メモリを大量に消費するものもある) の場合、メモリ プールをネストすることができます。ここでも、囲んでいるメモリ プールの特定の文字列に明示的にフラグを付けるか、メモリ プールが削除される前にそれらを移動する必要がある場合があります。

メモリ プール内のすべてのオブジェクトが同時に削除されるため、プール自体にファイナライザーをアタッチすることでファイナライザーを実装できます。プールは、プールが実際に解放される前に (逆の作成順序で) 順次実行されるファイナライザーの単純なリンクされたリストを保持できます。これにより、オブジェクトの状態を無効にしない限り、ファイナライザーは同じプール内の任意の他のオブジェクトを参照できます。大規模なファイナライザは、ファイル記述子などのメモリ以外のリソースを管理するためのものであるため、この制限はそれほど厳格ではありません。

APR には不変の文字列がありません (または、少なくとも、前回調べたときはありませんでした)。そのため、Apache は多くの不要な文字列のコピーを行うことになります。これは設計上の選択です。私はここでどちらか一方に投票しているわけではありません。

于 2013-02-03T02:55:57.797 に答える
0

不変の文字列構造体を宣言する「少し賢い」方法は次のようになります。

struct Rstring {
    size_t length;
    char s[0];
};

これは、長さゼロの配列ハックの実行です。Rstring以下のようにオブジェクトを割り当てることができます。

struct Rstring* alloc_rstring(const char* text) {
    size_t len = strlen(text);
    struct Rstring* ret = malloc(sizeof(Rstring) + len + 1));
    ret->length = len;
    memcpy(ret->s, text, len + 1);
    return ret;
}

free()文字列データは同じ割り当てに存在するため、単純な だけでそのような Rstring オブジェクトを解放します。

于 2013-02-03T02:40:56.470 に答える
0

本当に必要なのはC のガベージ コレクターです

LISP と Java のプログラマーは、ガベージ コレクションを当然のことと考えています。Boehm-Demers-Weiser ライブラリを使用すると、C および C++ プロジェクトでも簡単に使用できます。

于 2013-02-03T02:49:43.370 に答える