2

目的

私は、malloc / realloc / freeラッパー関数と、そのパラメーター(タイプvoid *)が割り当てられ管理されているライブ(まだ解放されていない)メモリに対応するかどうかを通知できる関数を提供する、より大きなプロジェクト用の小さなライブラリを作成しています。ライブラリのラッパー関数によって。この関数を。と呼びましょうisgood_memory

内部的には、ライブラリはハッシュテーブルを維持して、によって実行される検索isgood_memoryが適度に高速であることを確認します。ハッシュテーブルは、検索を可能にするためにポインタ(タイプの要素)を維持します。void *明らかに、値はハッシュテーブルに追加および削除され、割り当てられたものと解放されたものをそれぞれ最新の状態に保ちます。

ライブラリの移植性は私の最大の関心事です。これは、ほとんど準拠したC90(ISO / IEC 9899:1990)環境のみを想定するように設計されています...それ以上のものはありません。

質問

移植性が私の最大の関心事であるsizeof(void *) == sizeof(X)ため、ハッシュ関数についてはそれを想定できませんでした。したがって、私は値をバイトごとに文字列であるかのように扱うことに頼ってきました。これを実現するために、ハッシュ関数は次のようになります。

static size_t hashit(void *ptrval)
{
    size_t i = 0, h = 0;
    union {
        void *ptrval;
        unsigned char string[sizeof(void *)];
    } ptrstr;

    ptrstr.ptrval = ptrval;

    for (; i < sizeof(void *); ++i) {
        size_t byte = ptrstr.string[i];

        /* Crazy operations here... */
    }

    return (h);
}

この特定のフラグメントに関して、移植性に関してどのような懸念がありますか?ptrvalバイトごとにアクセスすることで、ファンキーな配置の問題が発生しますか?

4

5 に答える 5

1

かなりきれいに見えます。<inttypes.h>C99のヘッダー(他の場所でも利用できることが多い)を信頼できる場合は、 uintptr_t-の使用を検討してください。ただし、値をバイト単位でハッシュする場合は、バイトに分割することになり、実際の利点はありません。

于 2009-06-12T22:43:04.263 に答える
1

ほとんど正しい。ただし、潜在的な問題が1つあります。あなたが割り当てる

size_t byte = ptrstr.string[i];

* stringは、unsignedcharではなくcharとして定義されます。charsに署名しsize_tに署名していないプラットフォームでは、期待するかどうかわからない結果が得られます。charをunsignedcharに変更するだけで、よりクリーンになります。

于 2009-06-12T22:57:08.537 に答える
1

ここで行うように、unsignedcharの配列としてデータ型にアクセスできます。私が見ている主要な移植性の問題は、特定の場所を識別するビットパターンが一意ではないプラットフォームで発生する可能性があります。その場合、ビットパターンが異なるため、等しいハッシュを異なる場所と比較するポインターが得られる可能性があります。

なぜそれらは異なるのでしょうか?一つには、ほとんどのCデータ型には、値に関与しないパディングビットを含めることができます。ポインターにそのようなパディングビットが含まれているプラ​​ットフォームでは、パディングビットのみが異なる2つのポインターが同じ場所を指している可能性があります。(たとえば、OSは、物理アドレスだけでなく、ポインタの機能を示すためにいくつかのポインタビットを使用する場合があります。)別の例は、DOSの初期のfarメモリモデルで、farポインタはsegment:offsetと隣接するもので構成されていました。セグメントがオーバーラップしているため、segment:offsetはsegment + 1:offset-xと同じ場所を指すことができます。

とはいえ、今日一般的に使用されているほとんどのプラットフォームでは、特定の場所を指すビットパターンは確かに一意です。したがって、厳密に準拠している可能性は低いですが、コードは広く移植可能です。

于 2009-06-12T23:34:55.280 に答える
0

割り当てられたメモリを追跡する以外の理由でポインタ値が必要ない場合は、ハッシュテーブルを完全に削除して、以下の例のように割り当てられたメモリと一緒にマジックナンバーを保存してください。割り当てられたメモリの横にあるマジックナンバーは、それがまだ「生きている」ことを示しています。メモリを解放するときは、メモリを解放する前に、保存されているマジックナンバーをクリアします。

#pragma pack(1)
struct sMemHdl
{
   int magic;
   byte firstByte;
};
#pragma pack()

#define MAGIC 0xDEADDEAD
#define MAGIC_SIZE sizeof(((struct sMemHdl *)0)->magic)

void *get_memory( size_t request )
{
   struct sMemHdl *pMemHdl = (struct sMemHdl *)malloc(MAGIC_SIZE + request);
   pMemHdl->magic = MAGIC;
   return (void *)&pMemHdl->firstByte;
}

void free_memory ( void *mem )
{
   if ( isgood_memory(mem) != 0 )
   {
      struct sMemHdl *pMemHdl = (struct sMemHdl *)((byte *)mem - MAGIC_SIZE);
      pMemHdl->magic = 0;
      free(pMemHdl);
   }
}

int isgood_memory ( void *Mem )
{
   struct sMemHdl *pMemHdl = (struct sMemHdl *)((byte *)Mem - MAGIC_SIZE);
   if ( pMemHdl->magic == MAGIC )
   {
      return 1; /* mem is good */
   }
   else
   {
      return 0; /* mem already freed */
   }
}

これは少しハックっぽいかもしれませんが、私はハックっぽい気分になっていると思います...

于 2009-06-13T02:32:33.323 に答える
0

ポータビリティの観点からは、charやunsignedcharなどの整数やポインタなどの変数にアクセスすることは問題ありません。ただし、ハードウェアに依存するため、その逆は当てはまりません。質問が1つあります。ポインター自体をハッシュ値として使用するのではなく、ポインターを文字列としてハッシュするのはなぜですか(uintptr_tを使用)。

于 2009-06-13T16:41:13.877 に答える