Intereting Posts

C проблема – не могу понять, как назначить указатель на начало списка

У меня есть простое задание, которое профессор хочет, чтобы мы сделали. В основном, чтобы вытащить некоторые цифры из текстового файла и загрузить в связанный список. Я не хочу подробно разбираться в деталях, но у меня есть основной вопрос.

Он предоставил нам такую ​​функцию:

INTLIST* init_intlist( int n ) { INTLIST *lst; lst = (INTLIST *)malloc(sizeof(INTLIST)); lst->datum = n; lst->next = NULL; return lst; } 

Эта функция используется для инициализации связанного списка с первым элементом. Затем он попросил нас определить функцию с этой сигнатурой:

 int insert_intlist( INTLIST *lst, int n ) 

Поэтому я предполагаю, что он просто хочет, чтобы мы добавили в связанный список, поэтому я попробовал это:

 int insert_intlist( INTLIST *lst, int n ) { INTLIST* lstTemp; lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); lstTemp->datum = n; lstTemp->next = lst; lst = lstTemp; free(lstTemp); } 

Так что мой мыслительный процесс состоял в том, что он создает временный узел, назначает значение данных (Datum) и назначает следующий указатель, указывающий на то, на что указывает текущий указатель. Затем я переназначаю основной указатель на этот вновь созданный временный узел.

Таким образом, мы имеем, например, два узла:

[New Temp Node] -> [Prev Initialized Node]

Когда я просматриваю код, он выглядит великолепно …

Затем в основном у меня есть функция распечатки списка:

  while (lst!=NULL) { printf("The value is:%d", lst->datum); lst=lst->next; } 

Проблема в том, что это только печатает одну цифру (а именно первую цифру, которую я читаю из файла, которая, по моему мнению, является последней в списке или, по крайней мере, я думал, что она была последней в списке).

Но это должно продолжаться, поскольку у меня есть 10 цифр в файле. Я знаю, что код очень грязный, и я его очищу … вот моя главная функция, если кому-то нужна дополнительная информация:

 #include  #include  #include "intlist.h" int main(int argc, char *argv[]) { char c; /* Character read from the file. */ FILE* ptr; /* Pointer to the file. FILE is a structure defined in  */ int index=0; //INTLIST* aList[10]; //will use later /* Open the file - no error checking done */ ptr = fopen("1.txt","r"); /* Read one character at a time, checking for the End of File. EOF is defined in  as -1 */ if(ptr==NULL) { printf("Error: can't open file.\n"); /* fclose(file); DON'T PASS A NULL POINTER TO fclose !! */ return 1; } //aList[index] = malloc(sizeof(INTLIST)); WE NEED THIS LATER ON.... INTLIST *lst=NULL; while ((c = fgetc(ptr)) != EOF) { if (c != ' ') { //make sure it isnt a space int i = c - '0'; //get the value from the text file if(c=='\n') { // aList[index]=lst; // index++; // aList[index] = malloc(sizeof(INTLIST)); while (lst!=NULL) { printf("The value is:%d", lst->datum); lst=lst->next; } free(lst); free(aList[index]); return 0; //new line in the file //create another linked list } if (lst==NULL) lst = init_intlist(i); else insert_intlist( lst, i); } } fclose(ptr); system("PAUSE"); return 0; } 

Вот intlist.h для тех, кому это может понадобиться:

 #ifndef __intlist_h__ #define __intlist_h__ /* each entry in the list contains an int */ typedef struct intlist { int datum; struct intlist *next; } INTLIST; INTLIST *init_intlist( int n ); /* initializes the intlist with initial datum n */ int insert_intlist( INTLIST *lst, int n ); /* Inserts an int (n) into an intlist from the beginning*/ void list_append(INTLIST *list, void *datum); /* Inserts entry to the end of the list */ INTLIST* list_front(INTLIST *list); /*return the element at the front of the list, and remove it from the list*/ void list_map( INTLIST *list, void (*f)(void *) ); /*Applies a function to each element of the list */ void list_delete( INTLIST *list ); /* Deletes (and frees) all entries in the list */ #endif 

Здесь пара проблем.

Я начну с ошибки BAD:

 int insert_intlist( INTLIST *lst, int n ) { INTLIST* lstTemp; lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); lstTemp->datum = n; lstTemp->next = lst; lst = lstTemp; free(lstTemp); // <<<<< NO! } 

Вы все еще используете эту память, поэтому вы не можете ее освободить.


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

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

Попробуйте еще раз, вы не делаете ничего плохого.

Работа с кодом:

 int insert_intlist( INTLIST *lst, int n ) { INTLIST* lstTemp; lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); lstTemp->datum = n; lstTemp->next = lst; lst = lstTemp; free(lstTemp); } 

У этого есть пара проблем. Прежде всего, free(lstTemp) , по-видимому, освобождает узел, который вы только что вставили в список, который вы, вероятно, не хотите делать.

Во-вторых, вы передаете указатель на список в функцию, что означает, что функция не может изменить этот указатель, поэтому при назначении указателю вы меняете только свою локальную копию.

У вас есть два варианта: вы можете либо передать указатель на этот указатель (чтобы вы могли изменить исходный указатель), либо вы можете стать умнее и выяснить способ избежать необходимости (но я не буду сразу раздавать секрет …)

Эта строка:

 lst = lstTemp; 

Изменяет значение lst внутри функции. Он не будет распространяться обратно на копию указателя, который имеет вызывающий.

Вы можете использовать указатель-на-указатель, или если вы не можете изменить подпись функции, вставьте где-то, кроме главы списка.

Хотя типичный способ обработки этого состоит в том, чтобы не указывать на первый элемент в списке – скорее, у вас есть какая-то структура списка, которая содержит указатель на первый элемент и некоторую другую информацию о списке (скажем, сколько элементов она имеет). Затем вы передаете указатель на эту структуру.

В C параметры передаются в функции «по значению», то есть они копируются при вводе функции, и любые изменения, которые вы делаете с ними, не отражаются обратно вызывающему абоненту. Это означает, что когда вы изменяете lst и указываете на свою недавно выделенную память, он фактически не изменяет указатель вызывающего абонента на список.

EDIT: Как отметил dmckee, вы не должны освобождать память в своей функции вставки, поскольку вы все еще используете ее. Это определенно ошибка, но это не та, которая вызывает вашу проблему.

В C все передается по значению. Если вы хотите, чтобы функция что-то меняла, вам нужно передать свой адрес функции. Поскольку в int insert_intlist( INTLIST *lst, int n ) вы хотите изменить int insert_intlist( INTLIST *lst, int n ) списка, вам нужно передать указатель на него, т. INTLIST **lst Первый параметр должен быть INTLIST **lst (см. Ниже тоже). Но прототип функции задан и не может быть изменен.

Это означает, что вы не можете добавить число в начало списка – вызывающий не может знать, что вы это сделали. Таким образом, вам нужно пересечь список, на который указывает lst , а затем добавить новый узел по всей цепочке. Профессор, вероятно, хочет, чтобы вы добавили узел в конце, но он, возможно, попросил другое условие.

С этой информацией давайте посмотрим на комментарии к прототипам:

 /* Inserts an int (n) into an intlist from the beginning*/ int insert_intlist( INTLIST *lst, int n ); 

Комментарий или прототип неверны. Если ваш профессор дал вам этот файл, insert_intlist() не может быть написан для удовлетворения комментариев, так как он не может вернуть вызывающему абоненту новую голову. Прототипом должен быть:

 /* Inserts an int (n) into an intlist from the beginning and returns the new head */ INTLIST *insert_intlist( INTLIST *lst, int n ); 

Или же:

 /* Inserts an int (n) into an intlist from the beginning */ int insert_intlist( INTLIST **lst, int n ); 

(Обратите внимание на ** .)

Заголовок также имеет:

 /*return the element at the front of the list, and remove it from the list*/ INTLIST* list_front(INTLIST *list); 

Это правильно. Обратите внимание, что вам нужно изменить list_front() списка в list_front() , чтобы вы возвращали новую голову.

Наконец, вы не хотите free() что-либо в insert_intlist() . Вы хотите сохранить новый узел в списке, не так ли? Как только вызывающий абонент будет со списком, ему нужно будет вызвать list_delete() , который пересечет связанный список и освободит каждый узел.

Я согласен с Алоком. У меня такая же проблема / профессор. Я новичок в программировании на С, и я искал по всему Интернету формы и веб-страницы C для получения помощи. Я столкнулся с источником, который поддерживает Алока.

я использовал

INTLIST * list_add (INTLIST ** p, int i) {

 INTLIST *n; 
  n = (INTLIST *) malloc(sizeof(INTLIST)); if (n == NULL) return NULL; n->next = *p; /* the previous element (*p) now becomes the "next" element */ *p = n; /* add new empty element to the front (head) of the list */ n->datum = i; return p; } 

С моей стороны я могу пройти

Список INTLIST *

list_add (& list, 1); list_add (& list, 2);

поэтому, когда я печатаю список, он печатает 2 1

Профессор предложил следующее:

INTLIST * mylist [N];

Где N – количество строк вашего входного файла. Затем mylist [i] является указателем на i-й связанный список.

Хорошо Хорошо: создать для целей тестирования INTLIST * mylist [2];

Я называю те же функции:

list_add (& list [0], 1); list_add (& list [0], 2);

Это печатает 2 1 … Отлично,

Но когда я это делаю:

list_add (& list [1], 3); list_add (& list [1], 4);

Я получаю ошибку сегментации.