【问题标题】:C issue - Can't figure how to assign pointer to beginning of listC问题-无法弄清楚如何将指针分配给列表的开头
【发布时间】:2010-01-21 03:21:54
【问题描述】:

我有一个教授要我们做的简单任务。 基本上是从文本文件中提取一些数字并加载到链接列表中。 我不想谈太多细节,但我有一个基本问题。

他为我们提供了这样的功能:

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 个节点:

[新建临时节点] -> [上一个初始化节点]

当我单步执行代码时,它看起来很棒......

然后回到 main 我只有一个打印列表的函数:

                   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

【问题讨论】:

  • 你的教授给的原型是标题吗? int insert_intlist( INTLIST *lst, int n ); 带有 Inserts an int (n) into an intlist from the beginning 的评论是错误的:使用原型,这是无法做到的。详情见我的回答。
  • @Alok:你不能自然而然地做到这一点。 Jerry 明白了,将新节点放在第二个,然后交换有效负载。我没有仔细阅读并假设在后面添加。
  • @dmckee:啊哈!我喜欢这种规则的弯曲——尽管我想知道这是否是教练想要的。另一种可能性是返回值是int,所以该函数可能只是返回旧头部的有效负载,并将其替换为新值!这确实是一个简单的功能。

标签: c pointers


【解决方案1】:

这里有几个问题。

我将从一个 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!
 }

您仍在使用该内存,因此无法释放它。


其次,提供给您用于插入的原型无法返回列表的新前端,因此您无法更改列表的前端。这意味着您必须将新节点添加到 back,而不是像您所做的那样添加到前面。

另外,int 提供的返回类型可能意味着他期望列表中的节点数,这没问题,因为无论如何您都必须遍历列表才能找到后面。

再试一次,你的表现一点也不差。

【讨论】:

  • 好的,谢谢你的好话,让我看看我是否可以在列表开头添加?
  • ok dmckee 我要试试这个: int insert_intlist( INTLIST lst, int n ) { INTLIST lstTemp; lstTemp = (INTLIST )malloc(sizeof(INTLIST)); lstTemp->数据= n; INTLIST pTemp; pTemp=lst; while (pTemp->next != null) { pTemp = pTemp->next; } pTemp->下一个=lstTemp; lstTemp->下一个=NULL; }
  • 哇,感谢您提供的提示...当我通读答案时,我会接受,但 +1 是给我提示而不是答案!
  • @jmh86:我实际上认为 Jerry 比我更接近目标,唉。我没有阅读标题中的 cmets,而是根据我的直觉。
  • 对不起,我只是在读这个并且...如果文本文件包含 1 9 7 2 并且我他将 1 添加到链接列表然后他添加 9..the我看到在头部和交换值之后添加它的问题最终是他的链表是向后的,然后变成 2 7 9 1。因为你说将它添加到头部并交换数据值......所以这真的是他应该做?似乎将其添加到末尾是要走的路?我只是问,因为我不是 C 开发人员,但这就是我的看法?
【解决方案2】:

使用如下代码:

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) 似乎正在释放您刚刚插入列表中的节点,您可能不想这样做。

其次,您将指向列表的指针传递给函数——这意味着函数无法修改该指针,因此当您分配指针时,您只是在更改它的本地副本。

你有两个选择:你可以传入一个指向该指针的指针(这样你就可以修改原始指针),或者你可以变得聪明并想出一个避免需要的方法(但我不会放弃马上秘密...)

【讨论】:

  • 我不能修改函数签名,因为教授说我们不能这样做。我还评论了 free(lstTemp),但我仍然遇到同样的问题。
  • 是的——虽然这是 a 错误,但还不是您注意到的错误。如果您必须使用相同的函数签名,还有一种方法,但它有点棘手。基本思想是在列表的当前头之后添加一个新节点,然后在节点之间交换值以使它们按顺序返回。
  • @Jerry: 因为函数的返回类型是int,可能导师希望函数返回旧头部的payload,并用新值替换,这样返回就有意义了输入原型,大大简化了功能! :-)
  • 如果文本文件包含 1 9 7 2 并且我将 1 添加到链表然后他添加 9 ..我看到的问题是在头部和交换值最终是他的链表向后,然后变成 2 7 9 1。因为你说将它添加到头部旁边并交换数据值......所以这真的是他应该做的吗?似乎将其添加到末尾是要走的路?我只是问,因为我不是 C 开发人员,但这就是我的看法?
  • @JonH:这取决于他想要什么顺序。他最初试图将项目添加为新的头部项目,所以我告诉他一种可以维持生产顺序的方法。也可以将项目添加到末尾,但它给出了相反的顺序,并且需要更长的时间。如果您真的不关心顺序,您可以将每个项目立即添加到列表的头部之后,并且不要打扰交换。这给出了一个相当奇怪的顺序:第一个项目在前,其余的以相反的顺序排列。再说一次,如果你不关心顺序,你不应该使用链表开始。
【解决方案3】:

这一行:

lst = lstTemp;  

只改变函数内部lst 的值。它不会传播回调用者拥有的指针的副本。

您可以使用指向指针的指针,或者如果您无法更改函数签名,则插入列表头部以外的位置。

虽然处理此问题的典型方法是指向列表中的第一个元素 - 相反,您有某种列表结构,其中包含指向第一个元素的指针,以及其他一些有关列表的信息(例如,它有多少元素)。然后你传递一个指向那个结构的指针。

【讨论】:

  • 好的,我知道它在函数之后死了,所以我无法更改函数签名,你能给我更多关于在列表头部以外的地方插入的信息吗?
  • 您可以将列表遍历到最后 (while(listNode-&gt;next) listNode = listNode-&gt;next;),然后将其粘贴在那里 (listNode-&gt;next = newNode;)
【解决方案4】:

在 C 中,参数“按值”传递给函数,这意味着当您输入函数时它们会被复制,并且您对它们所做的任何更改不会反映回调用者。这意味着当您修改 lst 以指向新分配的内存时,它实际上并没有修改调用者指向列表的指针。

编辑:正如 dmckee 指出的那样,您不应该释放插入函数中的内存,因为您仍在使用它。这绝对是一个错误,但它不是导致你的问题的那个。

【讨论】:

  • 我想我的问题是因为我无法更改函数签名..如何处理这样的事情。
  • 正如其他人指出的那样,您将不得不在列表中的其他位置插入新节点。它可以位于末尾(从而保持数字的顺序),也可以将其添加为第二个节点。如果您想在最后添加它,您将不得不到达列表的末尾,然后使最后一个节点指向新分配的节点。如果你第二个添加它,你必须确保新节点指向之前是第二个节点的节点,然后让第一个节点指向新节点。
【解决方案5】:

在 C 中,一切都是按值传递的。如果你想让一个函数改变一些东西,你需要将它的地址传递给函数。由于在int insert_intlist( INTLIST *lst, int n ) 中,您想要更改列表头,您需要传递一个指向它的指针,即第一个参数应该是INTLIST **lst(不过也请参见下文)。但是函数原型是给定的,不能更改。

这意味着您不能将数字添加到列表的开头 - 调用者无法知道您这样做了。因此,您必须遍历lst 指向的列表,然后将新节点添加到链中的任何位置。教授可能希望您在最后添加节点,但他可能要求其他条件。

有了这些信息,我们来看看原型的 cmets:

/* 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(),这将遍历链表,并释放每个节点。

【讨论】:

  • 很好,很好。将帮助任何人理解 C :)。
【解决方案6】:

我同意阿洛克的观点。我有同样的问题/教授。我是 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 个链表的指针。

Okay Fine:为测试目的创建 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);

我得到一个分段错误..

【讨论】:

  • 当我尝试在 list_add() 期间打印列表时出现分段错误;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 2013-11-05
  • 2020-11-16
  • 1970-01-01
相关资源
最近更新 更多