我最近开始学习C,我正在尝试实现一个哈希图数据结构。 我目前正在将我的存储桶实现为动态调整数组支持列表的大小。 在测试我的bucket_remove函数时,我在测试代码中犯了一个错误,我很难理解。
这是我的
new_entry
功能:
struct Entry *new_entry(char *id)
{
struct Entry *e = (struct Entry *)malloc(sizeof(struct Entry));
e->id = id;
return e;
}
这是我的
test_remove
功能:
static char *test_remove()
{
struct Bucket *b = new_bucket();
for (int i = 0; i < 5; i++)
{
char id[8];
snprintf(id, 8, "entry-%d", i);
struct Entry *e = new_entry(id);
bucket_add(b, e);
}
printf("Entry 0 id: %s.\n", b->_entries[0]->id);
printf("Entry 1 id: %s.\n", b->_entries[1]->id);
// ... rest of code omitted for clarity
}
在每次迭代的循环内,都会使用正确的 id 创建一个新条目,但之前条目的 id 会更新为最新的 id。 所以,控制台打印:
Entry 0 id: entry-4.
Entry 1 id: entry-4.
我相信这与我将本地
id
缓冲区传递给我的 new_entity
函数的方式有关。每个条目必须接收指向同一内存的指针。但我不太明白为什么会这样。
这就是我对循环中代码的理解:
char id[8];
- 在堆栈中分配 8 个字符的内存,并将地址分配给 id
。snprintf(id, 8, "entry-%d", i);
- 指向 id
缓冲区的指针被传递给 snprintf
,后者用创建的字符串的字符填充它。struct Entry *e = new_entry(id);
- 指向 id
缓冲区的指针被传递到 new_entry
,其中在堆上分配新的 Entry 结构,并且 id
(指针)被分配给其 id
成员。bucket_add(b, e);
- 指向存储桶的指针和指向条目的指针被传递到 bucket_add
,并且条目(指针)被添加到存储桶的条目数组中。当发生新的迭代时,看起来就像分配了一段新的内存,并将其新的内存地址分配给本地范围内的新变量。那么之前的记忆是如何被覆盖的呢?
如果有人能澄清我的理解,我将不胜感激。
您将指针传递给
new_entry(char *id)
对于在堆栈上创建一个带有 id 字符串的内存地址的变量的计算机。
当您在
e->id = id;
中执行 new_entry()
时,您会在 e->id
中保存 id 字符串的内存地址,而不是字符串。
当您重新调用
snprintf(id, ...);
时,您将重新编写字符串,并将其保存在您的 char id[8];
中。
当您读取您的 ID 时,您会读取到指向
char id[8]
的指针,并且会读取您所做的最后更改。
我们可以通过打印所有元素id的指针地址来检查。
#include <stdio.h>
#include <stdlib.h>
struct Entry
{
char *id;
};
struct Entry *new_entry(char *id)
{
struct Entry *e = malloc(sizeof(struct Entry));
if (e == NULL)
return (NULL);
e->id = id;
return e;
}
int main(void)
{
struct Entry * b[2];
for (int i = 0; i < 2; i++)
{
char id[8];
id[0] = '0' + i;
id[1] = '\0';
struct Entry *e = new_entry(id);
if (e == NULL)
return (-1);
b[i] = e;
}
printf("Entry 0 id: %s.\n", b[0]->id);
printf("Entry 1 id: %s.\n", b[1]->id);
printf("Adress 0 id: %p.\n", b[0]->id);
printf("Adress 1 id: %p.\n", b[1]->id);
return (0);
}
产生该输出:
➜ test ./a.out
Entry 0 id: 1.
Entry 1 id: 1.
Adress 0 id: 0x7ffd7496bd30.
Adress 1 id: 0x7ffd7496bd30.
如果您想保存字符串的副本,您需要调用
strdup
(复制字符串)或 strcpy
(复制字符串):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Entry
{
char *id;
};
struct Entry *new_entry(char *id)
{
struct Entry *e = malloc(sizeof(struct Entry));
if (e == NULL)
return (NULL);
e->id = strdup(id);
if (e->id == NULL)
{
free(e);
return (NULL);
}
return e;
}
int main(void)
{
struct Entry * b[2];
for (int i = 0; i < 2; i++)
{
char id[8];
id[0] = '0' + i;
id[1] = '\0';
struct Entry *e = new_entry(id);
if (e == NULL)
return (-1);
b[i] = e;
}
printf("Entry 0 id: %s.\n", b[0]->id);
printf("Entry 1 id: %s.\n", b[1]->id);
printf("Adress 0 id: %p.\n", b[0]->id);
printf("Adress 1 id: %p.\n", b[1]->id);
return (0);
}
结果:
➜ test ./a.out
Entry 0 id: 0.
Entry 1 id: 1.
Adress 0 id: 0x556eb8f522c0.
Adress 1 id: 0x556eb8f52300.
同时投射你的
malloc()
通常是个坏主意。
并且不要忘记像
malloc()
或strdup
这样的函数调用可能会失败,你有责任处理它。