почему я получаю Invalid read of size 8
Моя цель — передать массив целых чисел функции, которая вернет указатель на последний 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.
Вы должны переосмыслить цикл создания. if ( i+1!=len )
не обязательно, у вас есть эквивалентное условие в цикле for
. Вам просто нужно немного изменить порядок вещей.
Ошибка не говорит о том, что размер недействителен. Он говорит, что считываемые данные недействительны (неинициализированы), и их размер составляет 8 байт.
Вы смешиваете связанный список с массивом?
@Neil, я пытаюсь преобразовать массив в связанный список
@ЕвгенийШ. можете ли вы объяснить это подробнее в ответе с фрагментом кода, пожалуйста
@Barmar Я предполагал, что будет по-другому
Это бессмысленно. Размер - это размер переменной-указателя, он должен содержать либо нулевые, либо ненулевые указатели.
К вашему сведению, измените int *next;
в структуре на struct Item *next;
, что и должно быть в первую очередь.
@WhozCraig, можешь объяснить, почему я должен это делать?
Поскольку int
не является struct Item
, для меня (и вашего кода) было бы достаточной причиной.
Я не понимаю, как этот код может создать эту ошибку, поскольку вы не прикасаетесь к элементам после их освобождения.
Всегда включайте и прислушивайтесь к предупреждениям вашего компилятора (например, используя -Wall -Wextra -pedantic
с помощью gcc). Должно быть struct Item { int num; struct Item *next; }; typedef struct Item Item;
. У вас все еще есть проблема после исправления этого (что, я думаю, приводит к неопределенному поведению)?
@ikegami Я сделал gcc -Wall -pedantic -ggdb3
, и это не показывает никаких предупреждений или ошибок
Вы уверены, что компилируете тот же код, который вы разместили здесь? Каждая версия gcc, которую я пробовал, дает warning: assignment to 'Item *' from incompatible pointer type 'int *'
, как и должно быть (это проблема, которую WhozCraig отметил выше). godbolt.org/z/WEvYqW7sr
Я также не могу воспроизвести какую-либо ошибку valgrind с вашим кодом. Опять же, вы уверены, что это именно та версия, которую вы компилируете и тестируете? (На самом деле это не может быть точно таким же, потому что вам не хватает #include <stdlib.h>
, что должно привести к тонне предупреждений.)
@KGBGUY Вы назначаете int *
из Item *
. Не существует компилятора, который бы не предупредил вас об этой серьезной ошибке. Примечательно, что gcc предупреждает вопреки вашему утверждению
Насколько я понимаю проблема была в строке printf
в основной функции, она печатает адрес нулевого указателя и из-за этого появляется ошибка
Здесь две основные ошибки:
printf('%p'...
вместо printf("%p"...
Вы пытаетесь printf()
tmp->next
после того, как уже освободили его в free_lst()
. Это означает, что вы уже освободили этот блок кучи, но все еще пытаетесь его прочитать. Вот почему valgrind выдает ошибку.
В одном отсутствуют обязательные заголовки.
typedef struct Item
См. обсуждение typedef.
Item * create_list(int * arr, int len, Item ** lst)
Первые два аргумента необходимы, а последний не используется. В самой функции одно прямое объявление указателя сбивает с толку и не будет работать для нулевого списка. Это можно упростить.
void free_lst(struct Item ** lst, int len)
Это освобождает список как массив. Это не имеет смысла, поскольку он преобразуется в связанный список.
int main()
См. Какое правильное объявление main()?.
Item * items[sizeof(arr)/sizeof(int)];
Зачем вам массив указателей на Item?
free_lst(items, sizeof(arr)/sizeof(int));
Один освобождает массив элементов, которые не были инициализированы. Я думаю, вы видите это поведение из valgrind
, потому что free
вызывается в неинициализированной памяти.
printf('%p\n',tmp->next);
См. когда я должен использовать одинарные кавычки? Один предполагает, что список не пуст и печатает второй элемент?
Один пытается толкать элемент в связанном списке, операция, которая эффективна, но код излишне сложен. Кроме того, когда вы отправляете динамически размещенный объект, имеет смысл использовать поп, чтобы освободить объект. Я модифицировал свой код с проверкой ошибок (предполагая соответствие POSIX) и изменениями, перечисленными выше.
#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;
}
Как вы думаете, почему размер нулевого указателя отличается от размера любого другого указателя?