如何覆盖字符串缓冲区而不修改它的早期使用

问题描述 投票:0回答:1

我最近开始学习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
函数的方式有关。每个条目必须接收指向同一内存的指针。但我不太明白为什么会这样。

这就是我对循环中代码的理解:

  1. char id[8];
    - 在堆栈中分配 8 个字符的内存,并将地址分配给
    id
  2. snprintf(id, 8, "entry-%d", i);
    - 指向
    id
    缓冲区的指针被传递给
    snprintf
    ,后者用创建的字符串的字符填充它。
  3. struct Entry *e = new_entry(id);
    - 指向
    id
    缓冲区的指针被传递到
    new_entry
    ,其中在堆上分配新的 Entry 结构,并且
    id
    (指针)被分配给其
    id
    成员。
  4. bucket_add(b, e);
    - 指向存储桶的指针和指向条目的指针被传递到
    bucket_add
    ,并且条目(指针)被添加到存储桶的条目数组中。

当发生新的迭代时,看起来就像分配了一段新的内存,并将其新的内存地址分配给本地范围内的新变量。那么之前的记忆是如何被覆盖的呢?

如果有人能澄清我的理解,我将不胜感激。

c pointers buffer
1个回答
0
投票

您将指针传递给

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
这样的函数调用可能会失败,你有责任处理它

© www.soinside.com 2019 - 2024. All rights reserved.