0

(C) realloc数组修改items指向的数据

你好,

我想分享一个不错的奇怪错误 ;-) 需要一些初步解释:

首先,我有一种字符串类型,PString它保存它们的大小(和一个哈希值),然后是一个带有字节的灵活数组成员。这是构造函数的类型和种类(最后的 printfl 语句是调试):

typedef struct {
   size_t   size;
   uint     hash;
   char     bytes[];
} PString;

// offset from start of pstring struct to start of data bytes:
static const size_t PSTRING_OFFSET = sizeof(size_t) + sizeof(uint);

PString * pstring_struct (string str, size_t size, uint hash) {
   // memory zone
   char *mem = malloc(PSTRING_OFFSET + size * sizeof(char));
   check_mem(mem);

   // string data bytes:
   memcpy(mem + PSTRING_OFFSET, str, size);
   mem[PSTRING_OFFSET + size] = NUL;

   // pstring struct:
   PString * pstr = (PString *) mem;
   pstr->size = size;
   pstr->hash = hash;

   printfl("*** str:'%s' (%u) --> pstr:'%s' (%u) 0x%X",
   str, size, pstr->bytes, pstr->size, pstr);   ///////////////////////
   return pstr;
}

[欢迎对这个结构发表任何评论:我完全不确定是否做对了,在这里。这是我第一次使用灵活的数组成员,我找不到在分配的结构中使用它们的示例。]

其次,这些 pstrings 存储在字符串池中,这意味着一组实现为哈希表。像往常一样,用于冲突的“桶”(在哈希和取模之后)是单元的简单链表,每个单元都包含一个 pstring指针和一个指向下一个单元的指针。唯一的特殊细节是单元格本身存储在一个数组中,而不是在堆上的任何地方分配 [1]。希望图片清晰。这是 的定义Cell

typedef struct SCell {
   PString        * pstr;
   struct SCell   * next;
} Cell;

一切似乎都运行良好,包括对池本身的一系列测试。现在,在测试 pstring 例程(搜索)时,我注意到一个字符串发生了变化。经过一番研究,我终于猜到问题与池增长有关,并且最终可以完全减少围绕单元阵列增长的问题(因此,在将单元重新分配到列表之前)。以下是围绕此增长的调试打印行,show_pool生成输出的例程副本(仅显示字符串)和输出本身:

static void pool_grow (StringPool * pool, uint n_new) {
    ...
   // Grow arrays:
   show_pool(pool);  /////////////////////
   pool->cells = realloc(pool->cells, pool->n_cells * sizeof(Cell));
   check_mem(pool->cells);
   show_pool(pool);  ////////////////////
   ...

static void show_pool (StringPool * pool) {
   if (pool->n == 0) {
      printfl("{}");
      return;
   }

   printf("pool          : {\"%s\"", pool->cells[0].pstr->bytes);

   PString * pstr;
   uint i;
   for (i = 1; i < pool->n; i++) {
      pstr = pool->cells[i].pstr;
      printf(", \"%s\"", pstr->bytes);
   }

   printl("}");
}

// output:
pool          : {"", "abc", "b", "abcXXXabcXXX"}
pool          : {"", "abc", "b", "abcXXXabcXXXI"}

如您所见,存储的最后一个字符串有一个额外的字节“I”。由于同时我只是调用 realloc,我发现自己在进一步调试时有点受阻;努力思考无助于揭开这个谜团。(请注意,单元格只保存 pstring指针,那么增长单元格数组如何改变字符串字节?)另外,我被以下事实吓到了,因为 printf 在神秘的“I”之后似乎有一个非常方便的 NUL停在那里。

谢谢你。你能帮我吗?

[1] 这里没有特别的理由这样做,使用字符串池。我通常这样做是为了免费获得一个有序的集合或地图,以及参考的位置。(唯一的开销是除了桶数组之外,单元数组必须增长,但可以通过预先确定维度来减少增长的数量。)

4

1 回答 1

2

由于size不包括空终止符,

   mem[PSTRING_OFFSET + size] = NUL;

是无效的。其他所有问题都源于此。

于 2012-12-14T17:55:09.627 回答