Я пытаюсь создать связанный список без динамического распределения и заполнить его числом от 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;
}
Вроде должно работать, но не работает, что я упускаю?
Один из способов избежать malloc
, когда количество узлов известно, — это работать с узлами в статическом массиве.
list = *(list.next);
Это перезапишет ваш узел содержимым неинициализированного нового узла. Любое значение, которое вы записываете в value
, впоследствии теряется. Даже если вы хотите избежать malloc
, вам нужно где-то получить память. Локальные переменные для этого не подходят.
@StephenNewell, потому что я не понимал, зачем нам это нужно, теперь я благодарю вас.
next
указатели вы жонглируете в нужном положении, но если бы вы шли по этому маршруту, вам было бы так же хорошо использовать «следующий» показатель (не редкость для таблиц символов мда, но все же.).
@WeatherVane Это хороший момент! Однако массив не обязательно должен быть статичным, он просто должен сохраняться, по крайней мере, до тех пор, пока он используется.
@CiaPan Уверен, что слово «статический» в этом комментарии не должно было быть буквальным ключевым словом static
. «статический» также может использоваться как противоположность «динамическому»..
@user3386109 user3386109 Это неточно - «статический» не противопоставляется «динамическому». Они не являются отрицаниями друг друга, а являются двумя из трех непересекающихся категорий, а третья — автоматический. Это локальные нестатические переменные. Можно также классифицировать переменные только на статические и динамические, в то время как динамические будут дополнительно разделены на автоматические и контролируемые (явно выделяемые семейством malloc
), но этот подход встречается гораздо реже.
@CiaPan Возможно, мы можем заменить «статический» на «глобальный охват» и двигаться дальше?
@CraigEstey Увы, нет. Это два не связанных между собой понятия. Рассмотрим int getNextInt() { static int n; return ++n; }
Переменная статическая (она существует и сохраняет свое значение на протяжении всего времени выполнения процесса), но, очевидно, нет в глобальной области видимости (доступна только локально, внутри этой одной функции).
@CiaPan Вы, кажется, не понимаете, что есть два разных значения слова «статический». В языке С «статический» относится к ключевому слову static
. В языке Английский «статический» противоположен «динамическому»..
@CiaPan не имеет значения, является ли массив автоматическим или static
здесь. Дело в том, что ни один из них не использует malloc
. Я мог бы усложнить проблему, сказав, что иногда использую динамически выделяемый массив, когда существует большое количество ссылок и повторного использования узлов. В этом случае комментарий WhozCraig хорош: используйте для ссылки индекс, а не адрес, а затем массив можно перераспределить, чтобы получить еще один фрагмент узлов. Вместо free
для несвязанного узла он переходит в связанный список доступных узлов, и когда узел необходим, он имеет приоритет над следующим элементом массива.
@WeatherVane Именно это я и сказал: не имеет значения, является ли массив статическим, он просто должен сохраняться, по крайней мере, до тех пор, пока он используется. EOT.
@CiaPan любая переменная, к которой вы обращаетесь, должна быть в области видимости. Здесь нет особых требований. Я не понимаю, почему ты не отпускаешь это.
Для размещения узлов без 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;
}
new
выходит за рамки каждой итерации вашегоfor
и уничтожается. Почему вы пытаетесь избежатьmalloc
?