初始化链表数组时写入无效

Invalid write when initializing an array of linked lists

所以我有这个函数分配和初始化 NULL 将是一个链表数组。我实际上想要这个函数 return 一个 NULL 指针数组,这样我以后可以用链表填充它。

static t_tokens **init_tokens_groups(size_t size)
{
    t_tokens **toks_groups;
    if (!(toks_groups = malloc(sizeof(toks_groups) * size + 1)))
        exit(EXIT_FAILURE);
    while (size + 1)
    {
        printf("size: %zu\n", size);
            toks_groups[size] = NULL;
            size--;
    }
    return (toks_groups);
}

它工作正常,但是当我 运行 我的程序(这是一个极简主义 shell)进入 Valgrind 时,

valgrind --track-origins=yes ./mysh

我运行喜欢这个:

==4914== Invalid write of size 8
==4914==    at 0x10AD6B: init_tokens_groups (tokens_split.c:39)
==4914==    by 0x10AE06: split_tokens (tokens_split.c:68)
==4914==    by 0x1093D9: prompt_loop (sh21.c:38)
==4914==    by 0x10944A: main (sh21.c:57)
==4914==  Address 0x4a508f8 is 8 bytes inside a block of size 9 alloc'd
==4914==    at 0x483A7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==4914==    by 0x10AD42: init_tokens_groups (tokens_split.c:35)
==4914==    by 0x10AE06: split_tokens (tokens_split.c:68)
==4914==    by 0x1093D9: prompt_loop (sh21.c:38)
==4914==    by 0x10944A: main (sh21.c:57)
==4914== 
==4914== Invalid write of size 8
==4914==    at 0x10AD6B: init_tokens_groups (tokens_split.c:39)
==4914==    by 0x10AE06: split_tokens (tokens_split.c:68)
==4914==    by 0x109369: dispatch (sh21.c:19)
==4914==    by 0x1093F6: prompt_loop (sh21.c:42)
==4914==    by 0x10944A: main (sh21.c:57)
==4914==  Address 0x4a509f8 is 8 bytes inside a block of size 9 alloc'd
==4914==    at 0x483A7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==4914==    by 0x10AD42: init_tokens_groups (tokens_split.c:35)
==4914==    by 0x10AE06: split_tokens (tokens_split.c:68)
==4914==    by 0x109369: dispatch (sh21.c:19)
==4914==    by 0x1093F6: prompt_loop (sh21.c:42)
==4914==    by 0x10944A: main (sh21.c:57)

我真的不明白它是从哪里来的,因为我只是在这个阶段初始化那些指向 NULL 的指针,而且我没有任何问题或错误(好吧,我没有检测到) 在程序中填充、操作和读取那些链表数组。我假设我正在用内存做一些奇怪的事情,但我不知道在哪里。

这个:

toks_groups = malloc(sizeof(toks_groups) * size + 1))

是错误的,并且在您第一次执行 toks_groups[size] = NULL; 时会导致问题(在分配区域之外)。 Valgrind 告诉您无效写入是 8 个字节,因为您系统上的指针大小是 8 个字节。

如果您想分配 size + 1 个元素,您应该将表达式放在括号中。事实上,你在做 sizeof(toks_groups) 也很奇怪,没有多大意义。您可能想要做的是:

toks_groups = malloc(sizeof(*toks_groups) * (size + 1)))

如果您只想分配 size 个元素(而不是 size + 1),则从 malloc() 中删除 + 1 并更改 while (...) 条件。您也根本不需要 while,如果您想将所有内容初始化为 NULL,您可以使用 calloc() 而不是 malloc()

此外,根据经验:

  1. 使用有意义的名称。当它显然不是一个大小,而只是一些元素时,调用 size 是令人困惑的。
  2. 使用正确的代码结构。使用 while 循环迭代一系列值确实违反直觉,并且很容易导致错误。

上述代码的更好版本如下:

static t_tokens **init_tokens_groups(size_t n)
{
    t_tokens **toks_groups;

    if (!(toks_groups = calloc(sizeof(*toks_groups) * (n + 1))))
        exit(EXIT_FAILURE);

    return toks_groups;
}

我仍然不确定你是否真的需要额外的元素,但你应该知道这一点。