(C)realloc数组按项目
修改数据指向您好,
我想分享一个很奇怪的错误;-)需要一些初步的解释:
首先,我有一种字符串PString
,它们保持它们的大小(和一个哈希值),然后是一个带有字节的灵活数组成员。下面是构造函数的类型和种类(最后的printfl语句是debug):
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 指针,那么如何增长单元格数组会改变字符串字节?)另外,我被大肆宣传的事实似乎是一个非常方便的NUL神秘的'我',因为printf停在那里。
谢谢。 你能帮忙吗?
[1]这里没有特别的理由,使用字符串池。我通常这样做是为了免费获得有序的集合或地图,以及参考的地方。 (唯一的开销是除了桶数组之外,单元格数组必须增长,但是可以通过预定义来减少增长的数量。)
答案 0 :(得分:2)
由于size
不包含空终止符,
mem[PSTRING_OFFSET + size] = NUL;
无效。其他每一个问题都源于此。