哪些可移植性问题与C中指针的字节级访问有关?

时间:2009-06-12 22:37:50

标签: c pointers hash alignment portability

目的

我正在为一个更大的项目编写一个小型库,它提供malloc / realloc / free包装器函数以及一个可以告诉你它的参数(类型void *)是否与live相对应的函数(尚未释放)由库的包装函数分配和管理的内存。我们将此函数称为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,我会遇到任何时髦的对齐问题吗?

5 个答案:

答案 0 :(得分:1)

看起来很干净。如果您可以依赖C99中的<inttypes.h>标头(通常在其他地方可用),那么请考虑使用uintptr_t - 但如果您想按字节方式对值进行散列,最终会将内容分解为字节并没有真正的优势。

答案 1 :(得分:1)

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

size_t byte = ptrstr.string[i];

* string定义为char,而不是unsigned char。在已签署字符和无符号size_t的平台上,它将为您提供您可能或可能不期望的结果。只需将您的char更改为unsigned char即可更清晰。

答案 2 :(得分:1)

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

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

所有这一切,在今天常用的大多数平台上,指向给定位置的位模式确实是唯一的。因此,即使不太可能严格遵守,您的代码也将具有广泛的可移植性。

答案 3 :(得分: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 */
   }
}

这可能有点骇人听闻,但我想我心情不好......

答案 4 :(得分:0)

从可移植性视图中访问诸如字符或无符号字符之类的整数或指针之类的变量。但反之则不然,因为它依赖于硬件。 我有一个问题,为什么要将指针作为字符串哈希而不是将指针本身作为哈希值(使用uintptr_t)?