C проблема - не могу понять, как назначить указатель на начало списка - PullRequest
4 голосов
/ 21 января 2010

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

Он дал нам такую ​​функцию:

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) и назначает следующий указатель, указывающий, куда указывает текущий указатель. Затем я переназначаю основной указатель на этот вновь созданный временный узел.

Таким образом, у нас есть, например, 2 узла:

[New Temp Node] -> [Предыдущий инициализированный узел]

Когда я шагаю по коду, он выглядит великолепно ...

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

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

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

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

#include <stdio.h>
#include <stdlib.h>
#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 <stdio.h> */
  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 <stdio.h>  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

Ответы [ 6 ]

4 голосов
/ 21 января 2010

Работа с кодом вроде:

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), кажется, освобождает узел, который вы только что вставили в список, что вы, вероятно, не хотите делать.

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

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

4 голосов
/ 21 января 2010

Несколько вопросов здесь.

Начну с ПЛОХОЙ ошибки:

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!
 }

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


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

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

Попросите другого, у вас совсем не плохо.

3 голосов
/ 21 января 2010

Эта строка:

lst = lstTemp;  

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

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

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

2 голосов
/ 21 января 2010

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

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

2 голосов
/ 21 января 2010

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

РЕДАКТИРОВАТЬ: Как указал dmckee, вы не должны освобождать память в вашей функции вставки, поскольку вы все еще используете ее. Это определенно ошибка, но она не , которая вызывает вашу проблему.

0 голосов
/ 22 января 2010

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

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

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);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...