为什么我得到这个无效的读取大小 8

Why do i get this Invalid read size of 8

为什么我得到一个 Invalid read of size 8

我的目标是将一个整数数组传递给一个函数,该函数将 return 指向链表中最后一个 Item 的指针,并将填充 Item 的数组指针和创建的每个项目的指针

一切正常,编译没有显示任何错误,运行程序也没有显示任何错误,但是当我使用 valgrind 时,我在下面的 valgrind 输出中得到错误

typedef struct Item
{
    int num ;   
    struct Item *next;  
}Item;


Item * create_list(int * arr, int len, Item ** lst)
{
    Item * tmpItem = malloc(sizeof(Item));

    for (int i=0; i < len; i++)
    {
        lst[i] = tmpItem;
        tmpItem->num = arr[i];
        if ( i+1!=len )
        {
            tmpItem->next = malloc(sizeof(Item));
            tmpItem = tmpItem->next;
        }
        else
            tmpItem->next = NULL;
    }
    return tmpItem;
}



void free_lst(Item ** lst, int len)
{
    for (int i =0; i < len; i++)
    {
        free(lst[i]);
    }
}
int main()
{
    int arr[] = {1,2,3,4,5};
    Item * items[sizeof(arr)/sizeof(int)];
    Item * tmp = create_list(arr, sizeof(arr)/sizeof(int), items);
    free_lst(items, sizeof(arr)/sizeof(int));
    printf('%p\n',tmp->next);

}

valgrind 输出

==12169== Invalid read of size 8
==12169==    at 0x109270: main (main.c:26)
==12169==  Address 0x4a59228 is 8 bytes inside a block of size 16 free'd
==12169==    at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==12169==    by 0x10939D: free_lst (list.c:45)
==12169==    by 0x10926B: main (main.c:24)
==12169==  Block was alloc'd at
==12169==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==12169==    by 0x10931C: create_list (list.c:24)
==12169==    by 0x109209: main (main.c:13)
==12169== 

我不明白为什么会出现这个错误,但我猜这是因为链表中的最后一个元素有一个空指针,这导致最后一个元素的长度为 8 个字节而不是 16 个字节

这里有两个主要错误:

  1. printf('%p'... 而不是 printf("%p"...

  2. free_lst() 中释放它后,您尝试 printf() tmp->next。这意味着您已经释放了这个堆块,但您仍在尝试从中读取。这就是 valgrind 抛出错误的原因。

一些问题

缺少一个必需项headers。

typedef struct Item

参见 discussion of typedef

Item * create_list(int * arr, int len, Item ** lst)

前两个参数是必需的,但最后一个未使用。在函数本身中,前向以一种令人困惑的方式声明指针,并且不适用于空列表。这可以简化。

void free_lst(struct Item ** lst, int len)

这会将列表作为数组释放。这没有意义,因为有人将其转换为 linked-list.

int main()

参见 What's the correct declaration of main()?

Item * items[sizeof(arr)/sizeof(int)];

为什么需要 array of pointer to Item

free_lst(items, sizeof(arr)/sizeof(int));

一个正在释放尚未初始化的元素数组。我认为您从 valgrind 看到了这种行为,因为 free 在未初始化的内存上被调用。

printf('%p\n',tmp->next);

看到when should I use single quotes了吗?假设列表不为空并打印第二个元素?

意图

一个人正在尝试 推送 linked-list 上的一个项目,这是一种高效的操作,但代码不必要地复杂。此外,当压入一个动态分配的 object 时,pop 释放 object 是有意义的。我已经通过错误检查(假设 POSIX-like 合规性)和上面列出的更改修改了一个代码。

#include <stdlib.h>
#include <stdio.h>

struct Item
{
    int num ;
    struct Item *next;
};

int main(void)
{
    int arr[] = {1,2,3,4,5};
    struct Item * head = 0, *cursor;
    /* `size_t` stddef, included by stdlib and stdio */
    size_t i = sizeof arr / sizeof *arr;
    int success = EXIT_SUCCESS; /* stdlib */
    while(i) { /* Push backwards. */
        struct Item *tmp;
        if(!(tmp = malloc(sizeof *tmp))) goto catch; /* stdlib */
        tmp->num = arr[--i];
        tmp->next = head;
        head = tmp;
    }
    for(cursor = head; cursor; cursor = cursor->next)
        printf("%d\n", cursor->num); /* stdio */
    goto finally;
catch:
    success = EXIT_FAILURE; /* stdlib */
    perror("to list"); /* stdio */
finally:
    while(head) { /* Pop off linked-list. */
        struct Item *const prev = head;
        head = head->next;
        free(prev); /* stdlib */
    }
    return success;
}