Создание связанного списка без malloc в C

Я пытаюсь создать связанный список без динамического распределения и заполнить его числом от 0 до 8, а затем распечатайте их, но каждый раз, когда я пытаюсь, они дают мне случайные значения.

#include <stdio.h>

struct listNode
{
    int value;
    struct listNode *next;
};

int main()
{
    struct listNode list;
    struct listNode *head = &list;
    int i;

    for (i = 0; i < 9; i++)
    {
        list.value = i;
        struct listNode new;
        list.next = &new;
        list = *(list.next);
    }

    for (i = 0; i < 9; i++)
    {
        printf("%d, ", (*head).value);
        head = (*head).next;
    }

    return 0;
}

Вроде должно работать, но не работает, что я упускаю?

new выходит за рамки каждой итерации вашего for и уничтожается. Почему вы пытаетесь избежать malloc?
Stephen Newell 22.03.2022 19:53

Один из способов избежать malloc, когда количество узлов известно, — это работать с узлами в статическом массиве.

Weather Vane 22.03.2022 19:55
list = *(list.next); Это перезапишет ваш узел содержимым неинициализированного нового узла. Любое значение, которое вы записываете в value, впоследствии теряется. Даже если вы хотите избежать malloc, вам нужно где-то получить память. Локальные переменные для этого не подходят.
Gerhardh 22.03.2022 20:00

@StephenNewell, потому что я не понимал, зачем нам это нужно, теперь я благодарю вас.

HishamS 22.03.2022 20:01
"Я пытаюсь создать связанный список без динамического размещения" - потому что ?? Я имею в виду, я полагаю, вы можете сделать это с фиксированным массивом узлов, чьи next указатели вы жонглируете в нужном положении, но если бы вы шли по этому маршруту, вам было бы так же хорошо использовать «следующий» показатель (не редкость для таблиц символов мда, но все же.).
WhozCraig 22.03.2022 20:02

@WeatherVane Это хороший момент! Однако массив не обязательно должен быть статичным, он просто должен сохраняться, по крайней мере, до тех пор, пока он используется.

CiaPan 22.03.2022 20:03

@CiaPan Уверен, что слово «статический» в этом комментарии не должно было быть буквальным ключевым словом static. «статический» также может использоваться как противоположность «динамическому»..

user3386109 22.03.2022 20:47

@user3386109 user3386109 Это неточно - «статический» не противопоставляется «динамическому». Они не являются отрицаниями друг друга, а являются двумя из трех непересекающихся категорий, а третья — автоматический. Это локальные нестатические переменные. Можно также классифицировать переменные только на статические и динамические, в то время как динамические будут дополнительно разделены на автоматические и контролируемые (явно выделяемые семейством malloc), но этот подход встречается гораздо реже.

CiaPan 22.03.2022 21:28

@CiaPan Возможно, мы можем заменить «статический» на «глобальный охват» и двигаться дальше?

Craig Estey 22.03.2022 21:36

@CraigEstey Увы, нет. Это два не связанных между собой понятия. Рассмотрим int getNextInt() { static int n; return ++n; } Переменная статическая (она существует и сохраняет свое значение на протяжении всего времени выполнения процесса), но, очевидно, нет в глобальной области видимости (доступна только локально, внутри этой одной функции).

CiaPan 22.03.2022 21:45

@CiaPan Вы, кажется, не понимаете, что есть два разных значения слова «статический». В языке С «статический» относится к ключевому слову static. В языке Английский «статический» противоположен «динамическому»..

user3386109 22.03.2022 21:54

@CiaPan не имеет значения, является ли массив автоматическим или static здесь. Дело в том, что ни один из них не использует malloc. Я мог бы усложнить проблему, сказав, что иногда использую динамически выделяемый массив, когда существует большое количество ссылок и повторного использования узлов. В этом случае комментарий WhozCraig хорош: используйте для ссылки индекс, а не адрес, а затем массив можно перераспределить, чтобы получить еще один фрагмент узлов. Вместо free для несвязанного узла он переходит в связанный список доступных узлов, и когда узел необходим, он имеет приоритет над следующим элементом массива.

Weather Vane 22.03.2022 23:23

@WeatherVane Именно это я и сказал: не имеет значения, является ли массив статическим, он просто должен сохраняться, по крайней мере, до тех пор, пока он используется. EOT.

CiaPan 23.03.2022 08:21

@CiaPan любая переменная, к которой вы обращаетесь, должна быть в области видимости. Здесь нет особых требований. Я не понимаю, почему ты не отпускаешь это.

Weather Vane 23.03.2022 10:04
Стоит ли изучать 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
14
109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для размещения узлов без malloc также есть возможность использовать для этой цели стек. Это интересно только как небольшое упражнение, но в реальном коде было бы плохой идеей.

Идея состоит в том, что всякий раз, когда необходимо выделить новый узел, вы делаете рекурсивный вызов, а затем продолжаете остальную логику программы без возврата из этого рекурсивного вызова. Только когда программа может выйти, вы избавитесь от рекурсии. Это не очень удобно для работы, приводит к трате памяти, так как в этом стеке находятся не только выделенные узлы, но и некоторые другие данные, которые больше не нужны. Кроме того, при удалении узла из списка повторное использование пространства невозможно, если только вы не поддерживаете также свободный список.

Итак, опять же, это не лучшая практика, и действительно следует использовать кучу для такого динамического распределения. Я надеюсь, что этот отказ от ответственности был достаточно сильным, чтобы позволить мне опубликовать этот фрагмент кода, реализующий эту идею:

#include <stdio.h>

struct listNode
{
    int value;
    struct listNode *next;
};

void printList(struct listNode *head) {
    while (head != NULL) {
        printf("%d -> ", head->value);
        head = head->next;
    }
    printf("NULL\n");
}

void deleteFromList(struct listNode **headPtr, struct listNode **tailPtr, int value) {
    while (*headPtr != NULL && (*headPtr)->value == value) {
        *headPtr = (*headPtr)->next;
    }
    if (*headPtr == NULL) {
        *tailPtr = NULL;
        return;
    }
    struct listNode *current = *headPtr;
    while (current->next != NULL) {
        if (current->next->value == value) {
            current->next = current->next->next;
        } else {
            current = current->next;
        }
    }
    *tailPtr = current;
}

void appendToList(struct listNode **headPtr, struct listNode **tailPtr, struct listNode *new) {
    new->next = NULL;
    if (*tailPtr != NULL) {
        (*tailPtr)->next = new;
    } else {
        *headPtr = new;
    }
    *tailPtr = new;
}

// This is the main program's logic, but it gets called recursively whenever a node
//   is inserted into the list:
void menu(struct listNode *head, struct listNode *tail) {
    struct listNode new;
    char choice;
    while (1) {
        printf("[i]nsert, [d]elete, [p]rint, [e]xit: ");
        scanf(" %c", &choice);
        switch(choice) {
            case 'p':
                printList(head);
                break;
            case 'i':
                printf("insert value: ");
                scanf(" %d", &new.value);
                appendToList(&head, &tail, &new);
                menu(head, tail); // Recursion
                return;
            case 'd':
                printf("delete value: ");
                scanf(" %d", &new.value);
                deleteFromList(&head, &tail, new.value); 
                break;
            case 'e':
                return;
        } 
    }
}
    
int main()
{
    menu(NULL, NULL);
    return 0;
}

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