2012-03-23 12 views
3

私は現在、Cでスタック実装を書いています。可能な限り小さなメモリを使用し、可能な限り高速にする必要があります。私はこのためにvoidポインターを使うことにしました。そして、私の錆びたCにもかかわらず比較的速く動作しました。しかし実際にはそれを使うのはかなり醜いです。C:voidポインタを使用する一般的なデータ構造のためのより良い構文ですか?

スタックをテストするために、整数をループでプッシュします。実際にはintをvoidポインタとして渡すことが問題です。

for (int i = 0; i < 20; i++){ 
    stack_push(s, &i); //s is the stack_t pointer 
} 

しかし精密検査でiが破壊的に各ステップごとに更新されているので、これは明らかに、機能しません。

私の最初の傾きが&を使用していました。スタック内のすべての要素は20となります。

私は以来、非常に醜いソリューションになってきました

for (int i = 0; i < 20; i++){ 
    int* p = malloc(sizeof(void*)); 
    *p = i; 
    stack_push(s, p); 
} 

このを動作しますが、二つの問題を提示:

  1. それは私が心配する必要は醜い

  2. ですスタックのすべての要素のメモリ管理について(そして何らかの理由で手動でスタックを歩いて、すべての要素を解放することは、まだメモリリーク...)

組合と不要なメモリを無駄にしてはまだ速いことなくこれを行うには良い方法はありますか?ありがとう。

+1

スタックからアイテムをポップアップすると、どのように取り出したのかはどのようにわかりますか?各スタックには複数のタイプがありますが、ポインタだけではどのタイプのアイテムであるかを知ることはできません。 – Jon

+0

スタックは何のために使用されますか?大まかに言えば、スタックはスタックポインタを下に移動するだけでメモリを解放することができます。しかし、スタックにヒープへのポインタが含まれていれば、それでもまだスタックを解放する必要があります。 – Douglas

+0

あなたがここで何を求めているのか分かりません。あなたがプッシュするポインタは通常、単純な整数へのポインタではなく、いくつかの種類のオブジェクトへの有効なポインタです。呼び出し元がそれらのオブジェクトのメモリを管理する責任はありませんか?または、スタックの実装を検討している場合は、ポインタの上で責任を採用し、スタックが破壊された場合はフリーですか? –

答えて

1

タイプが混在する場合は、タイプが何であるかを知る必要があります。

型を正しいフォームにキャストするスタックのクライアント(またはユーザー)であれば、それでも問題はありませんが、そのサイズはスタックで使用できる必要があります。

呼び出し側は、あまりにも、インターフェースは次のようになりますことを知っている場合:

無効プッシュ(のconst int型のサイズ、void *型の値)。 void pop(const int size、void ** valueptr);

一般的には堅牢ではないと思いますが、コンパイラを作成している場合は、Cコンパイラと同じように正しいコードをすべて生成できます。

Internalyでは、私は2つのバージョン、1つはデバッグ用、もう1つはサイズの値を保持し、もう1つは「パフォーマンス」用ではありません。私は最初にデバッグを始めました。

それは

  • 何かで管理する必要性を割り当てられたスペースを余分なスペースを使用しています

    1. ので、私はポインタの考えを捨てると思います。

    スタックに値が格納されている場合は、一度ポップされるとそれには責任がありません。

    実装:私は何がしたいことは、あなたがそれにしたいすべての種類のデータを置くことができるスタックで、誤解していなかった場合

    unsigned char space[BIG]; 
    unsigned char *stack = &space[0]; 
    int top = 0; 
    void push(const int size, void* value) { 
        unsigned char* valp = (char *)value; 
        *(int *)stack = size; 
        stack += sizeof(int); 
        for (int i=0; i<size; ++i) { 
         *stack++ = *val++; 
        } 
    } 
    

    ポップは逆の、

  • 2

    です。ここでは "struct any"という考え方があります。私はこれが助けると思う。

    union multitype 
    { 
        int  asInt; 
        void *asVPtr; 
        TYPE_A *asA; 
        TYPE_B *asB; 
        ... 
    } 
    
    typedef struct _any { 
        int    type; 
        union multitype value; 
    } Any; 
    
    void any_put_int(Any *pAny, int value); 
    void any_put_v_ptr(Any *pAny, void *value); 
    void any_put_typeA_ptr(Any *pAny, TYPE_A *value); 
    void any_put_typeB_ptr(Any *pAny, TYPE_B *value); 
    ... 
    
    int  any_get_int(Any *pAny); 
    void* any_get_v_ptr(Any *pAny); 
    TYPE_A* any_get_typeA_ptr(Any *pAny); 
    TYPE_B* any_get_typeB_ptr(Any *pAny); 
    ... 
    

    struct Anyに基づいてスタックのインプリケーションを取得した場合、質問のコードはこのようになります。

    for (int i = 0; i < 20; i++){ 
        Any anAny; 
        any_put_int(&anAny, i); 
        stack_push(s, anAny); // or stack_push(s, &anAny); 
    } 
    
    +0

    私の問題は、私の知る限りでは、コンパイラはすべての型を最大のタイプ。 4や何バイトかはいくつかの要素で大きな問題ではありませんが、1000+では間違いなく楽しいです。 – Rotten194

    +0

    @ Rotten194。ほとんどの場合、struct Anyは8バイトのメモリ、4バイトの型、4バイトのデータアドレスを取ります。ユニオンマルチタイプの型は等しい長さであることに注意してください.1つを除いてポインタは整数です。ほとんどのデータ型のメモリを維持する必要があります。データ全体をスタックに入れると、2つのメモリコピーが必要になります。データが大きければ、それも楽しいことではありません。データのサイズが小さい場合を除き、コンテナがデータそのもの以外のアドレスを保持する方がよいでしょう。スタック内のオブジェクトごとに4バイト余分* VS *重複データと余分なメモリコピー。これは実際の状況によって異なります。 –

    関連する問題