Почему я получаю этот недопустимый размер чтения 8

почему я получаю 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.

Как вы думаете, почему размер нулевого указателя отличается от размера любого другого указателя?

Barmar 16.05.2022 18:21

Вы должны переосмыслить цикл создания. if ( i+1!=len ) не обязательно, у вас есть эквивалентное условие в цикле for. Вам просто нужно немного изменить порядок вещей.

Eugene Sh. 16.05.2022 18:22

Ошибка не говорит о том, что размер недействителен. Он говорит, что считываемые данные недействительны (неинициализированы), и их размер составляет 8 байт.

Barmar 16.05.2022 18:22

Вы смешиваете связанный список с массивом?

Neil 16.05.2022 18:23

@Neil, я пытаюсь преобразовать массив в связанный список

KGBGUY 16.05.2022 18:28

@ЕвгенийШ. можете ли вы объяснить это подробнее в ответе с фрагментом кода, пожалуйста

KGBGUY 16.05.2022 18:29

@Barmar Я предполагал, что будет по-другому

KGBGUY 16.05.2022 18:31

Это бессмысленно. Размер - это размер переменной-указателя, он должен содержать либо нулевые, либо ненулевые указатели.

Barmar 16.05.2022 18:32

К вашему сведению, измените int *next; в структуре на struct Item *next;, что и должно быть в первую очередь.

WhozCraig 16.05.2022 18:35

@WhozCraig, можешь объяснить, почему я должен это делать?

KGBGUY 16.05.2022 18:37

Поскольку int не является struct Item, для меня (и вашего кода) было бы достаточной причиной.

WhozCraig 16.05.2022 18:41

Я не понимаю, как этот код может создать эту ошибку, поскольку вы не прикасаетесь к элементам после их освобождения.

user253751 16.05.2022 18:58

Всегда включайте и прислушивайтесь к предупреждениям вашего компилятора (например, используя -Wall -Wextra -pedantic с помощью gcc). Должно быть struct Item { int num; struct Item *next; }; typedef struct Item Item;. У вас все еще есть проблема после исправления этого (что, я думаю, приводит к неопределенному поведению)?

ikegami 16.05.2022 19:07

@ikegami Я сделал gcc -Wall -pedantic -ggdb3, и это не показывает никаких предупреждений или ошибок

KGBGUY 16.05.2022 19:20

Вы уверены, что компилируете тот же код, который вы разместили здесь? Каждая версия gcc, которую я пробовал, дает warning: assignment to 'Item *' from incompatible pointer type 'int *', как и должно быть (это проблема, которую WhozCraig отметил выше). godbolt.org/z/WEvYqW7sr

Nate Eldredge 16.05.2022 19:24

Я также не могу воспроизвести какую-либо ошибку valgrind с вашим кодом. Опять же, вы уверены, что это именно та версия, которую вы компилируете и тестируете? (На самом деле это не может быть точно таким же, потому что вам не хватает #include <stdlib.h>, что должно привести к тонне предупреждений.)

Nate Eldredge 16.05.2022 19:28

@KGBGUY Вы назначаете int * из Item *. Не существует компилятора, который бы не предупредил вас об этой серьезной ошибке. Примечательно, что gcc предупреждает вопреки вашему утверждению

ikegami 16.05.2022 20:28

Насколько я понимаю проблема была в строке printf в основной функции, она печатает адрес нулевого указателя и из-за этого появляется ошибка

KGBGUY 17.05.2022 07:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
18
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Здесь две основные ошибки:

  1. printf('%p'... вместо printf("%p"...

  2. Вы пытаетесь 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;
}

Другие вопросы по теме