【问题标题】:What portability issues are associated with byte-level access to pointers in C?哪些可移植性问题与对 C 中指针的字节级访问相关联?
【发布时间】:2009-06-12 22:37:50
【问题描述】:

目的

我正在为一个更大的项目编写一个小库,它提供 malloc/realloc/free 包装函数以及一个可以告诉您其参数(void * 类型)是否对应于 live(尚未已释放)由库的包装函数分配和管理的内存。让我们将此函数称为isgood_memory

在内部,库维护一个哈希表以确保isgood_memory 执行的搜索相当快。哈希表维护指针 valuesvoid * 类型的元素)以使搜索成为可能。显然,从哈希表中添加和删除值是为了使其分别与已分配的内容和已释放的内容保持同步。

库的可移植性是我最关心的问题。它被设计成只假设一个大部分符合 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 会遇到任何奇怪的对齐问题吗?

【问题讨论】:

  • 字节序可能是个问题
  • 不是真的;它仅用于程序内的散列,因此字节序是固定的,应用哪种字节序无关紧要。
  • 我刚刚将它从一个小端平台移植到一个大端平台(我可以访问的 Sun Microsystems 服务器)。一切似乎都运行良好。
  • Endianness 几乎肯定会改变为 same 指针值计算的哈希值,但是因为您不会在任何指针上都有 same 指针值其他平台(一般来说)这应该不是问题。如果是,那么您遇到了不同的问题。

标签: c pointers hash alignment portability


【解决方案1】:

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

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

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

【讨论】:

  • 我是否可以编制一份规模合理的此类平台列表?它们是否有任何明确的特征可以(例如)帮助我在 google 上进行搜索?
  • 您想要一个指针位模式是否唯一的平台列表?我认为没有可以用来搜索的常用术语,但总的来说,主流的类似工作站的平台(运行 Windows、Solaris、HP-UX、Linux 等操作系统)往往具有平坦的地址空间和独特的位模式。在嵌入式世界中,或者使用实验机器,您可能会获得更多定制硬件,并且情况可能会有所不同。 C 标准允许这样的平台,但我不知道今天有多少这样的平台在使用。
  • (这可能是一个愚蠢的问题,但我需要确定一下。)假设我们有两个指针值(均为 void * 类型),分别命名为 A 和 B。 A 和 B 具有相同的底层位模式,但实际上引用内存中不同位置的情况?
  • 如果你需要一个绝对确定的答案,我不是合适的人选。您可以在新闻组 comp.std.c 上询问,那里有更多的语言律师类型,其中一些人曾在起草标准的委员会任职。但我相信答案是否定的,答案将基于定义对象值的标准部分。您可以拥有不参与值的对象​​的位,但我认为没有任何基础需要比位更多的值。如果这是错误的,太多像 memcpy 这样的东西会被破坏。
【解决方案2】:

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

【讨论】:

  • 定义uintptr_t的C99头文件是,而不是;恰好 被指定为包含 中的各种整数类型定义了 imaxdiv_t 类型和用于 printf/scanf 的格式说明符的几个宏。
  • 定义了 包含 ,你有 没有 远比你有 而不是 。但是是的,你是对的,如果你只有 ,那么你就不需要
【解决方案3】:

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

size_t byte = ptrstr.string[i];

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

【讨论】:

  • 感谢您的建议。这很有趣......我的代码中实际上有 unsigned char,但是当我在这里输入它时忽略了 unsigned。我更正了上面的代码片段。
【解决方案4】:

如果除了跟踪分配的内存之外,由于其他原因不需要指针值,为什么不完全摆脱哈希表,只存储一个幻数以及分配的内存,如下例所示。分配的内存旁边出现的幻数表明它仍然“活着”。释放内存时,请先清除存储的幻数,然后再释放内存。

#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 */
   }
}

这可能有点骇人听闻,但我想我的心情很骇人……

【讨论】:

    【解决方案5】:

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

    【讨论】:

    • uintptr_t 在 C90 (ISO/IEC 9899:1990) 中可用吗?
    • uintptr_t 不是标准 C90 的一部分,但被许多编译器支持并在 stdint.h 中定义
    • 较大的项目对符合 C90 标准非常严格,所以我几乎没有其他选择。
    • 总有一种工具可以分配/转换指向整数的指针。
    猜你喜欢
    • 2021-12-03
    • 2013-10-13
    • 2021-05-22
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多