首页 > 解决方案 > C中的胖指针

问题描述

我已经阅读了有关Cello 胖指针库的信息,并想知道在 C 中是否实际上允许在某些数据前添加标头,例如:

struct header
{
  size_t length;
};

char* create_string(const char* cstr)
{
  void* ret = malloc(sizeof(struct header)+strlen(cstr)+1);
  ((struct header*)ret)->length = strlen(cstr);
  strcpy((char*)ret + sizeof(struct header), cstr);
  return (char*)ret + sizeof(struct header);
}

size_t get_length(const char* sized_string)
{
  struct header* tmp = sized_string - sizeof(struct header);
  return tmp->length;
}

此示例是一个字符串,但它可以是存储的任何其他内容,甚至可能不是数组,在这种情况下,标头可能具有不同类型的元数据。

我知道 sds 使用灵活的数组成员,但这需要 c99 并且不如这种方法灵活(除非您只是使用通用 char 数组并根据需要重新转换它)。

特别是在这个问题中,人们说以下内容实际上是不可移植的:

struct header {
  size_t len;
  unsigned char data[1];
};

因为访问第一个元素之外的数据是 UB。另一件事是这些线条((struct header*)ret)->length = strlen(cstr);struct header* tmp = sized_string - sizeof(struct header);我看起来不对(因为指针转换),我没有看到更好的方法来编写它们。

标签: cpointerslanguage-lawyerundefined-behavior

解决方案


您的示例仅从固定偏移量放置和读取。所以至少指针算术是完全合法的。至少只要类型只是一个char.

但是有问题的是对齐。这还没有违反语言标准,但实际数据的对齐方式比 x86 上的编译器默认提供的对齐方式更差。

因此,如果编译器默认对齐到 8 个字节(x86 上的 gcc 和 msvc)或 16 个字节(x64 上),则您的示例代码仅提供一半对齐。

这可能是非法的,如果编译器假设内存对齐(否则这将通过匹配malloc实现以及堆栈布局和结构中的填充来确保)。根据架构,它甚至可能导致错误,因为指令可能需要最小对齐。


推荐阅读