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

大部分是正确的。不过,有一个潜在的问题。你分配

size_t byte = ptrstr.string[i];

*string 被定义为 char,而不是 unsigned char。在已签名的字符和未签名的 size_t 的平台上,它会为您提供您可能期望或可能不期望的结果。只需将您的 char 更改为 unsigned char,这样会更干净。

于 2009-06-12T22:57:08.537 回答
1

您可以像在此处一样将数据类型作为 unsigned char 数组访问。我看到的主要可移植性问题可能发生在识别特定位置的位模式不是唯一的平台上 - 在这种情况下,您可能会获得将相同哈希值与不同位置进行比较的指针,因为位模式不同。

为什么他们会有所不同?好吧,一方面,大多数 C 数据类型都允许包含不参与值的填充位。指针包含此类填充位的平台可能具有两个仅在填充位不同的指针指向相同的位置。(例如,操作系统可能使用一些指针位来指示指针的功能,而不仅仅是物理地址。)另一个例子是 DOS 早期的远内存模型,其中远指针由段:偏移量和相邻的段重叠,因此segment:offset 可以指向与segment+1:offset-x 相同的位置。

尽管如此,在当今常用的大多数平台上,指向给定位置的位模式确实是独一无二的。因此,您的代码将具有广泛的可移植性,即使它不太可能严格符合。

于 2009-06-12T23:34:55.280 回答
0

从可移植性的角度来看,访问整数或指针等变量(如字符或无符号字符)不是问题。但反过来则不然,因为它依赖于硬件。我有一个问题,为什么要将指针作为字符串散列而不是使用指针本身作为散列值(使用 uintptr_t)?

于 2009-06-13T16:41:13.877 回答
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 */
   }
}

这可能有点hackish,但我想我的心情很糟糕......

于 2009-06-13T02:32:33.323 回答