【问题标题】:C generic linked-listC 通用链表
【发布时间】:2012-06-25 18:32:36
【问题描述】:

我有一个包含 void* 类型数据的通用链表我正在尝试使用 struct employee 类型填充我的列表,最终我也想销毁对象 struct employee。

考虑这个通用的链表头文件(我已经用 char* 类型对其进行了测试):

struct accListNode                 //the nodes of a linked-list for any data type
{
  void *data;                     //generic pointer to any data type
  struct accListNode *next;       //the next node in the list
};

struct accList                    //a linked-list consisting of accListNodes
{
  struct accListNode *head;
  struct accListNode *tail;
  int size;
};

void accList_allocate(struct accList *theList);           //allocate the accList and set to NULL
void appendToEnd(void *data, struct accList *theList);    //append data to the end of the accList
void removeData(void *data, struct accList *theList);         //removes data from accList
  --------------------------------------------------------------------------------------

考虑员工结构

struct employee 
{ 
   char name[20]; 
   float wageRate; 
} 

现在考虑这个将从 main() 调用的示例测试用例:

    void test2()
    {
      struct accList secondList;
      struct employee *emp = Malloc(sizeof(struct employee));
      emp->name = "Dan";
      emp->wageRate =.5;

      struct employee *emp2 = Malloc(sizeof(struct employee));
      emp2->name = "Stan";
      emp2->wageRate = .3;

      accList_allocate(&secondList);
      appendToEnd(emp, &secondList);
      appendToEnd(emp2, &secondList);

      printf("Employee: %s\n", ((struct employee*)secondList.head->data)->name);   //cast to type struct employee
      printf("Employee2: %s\n", ((struct employee*)secondList.tail->data)->name);  
    }

为什么我在下面发布的答案可以解决我的问题?我相信它与指针和内存分配有关。我使用的函数 Malloc() 是一个自定义 malloc,用于检查是否返回了 NULL。

这是我的整个通用链表实现的链接:https://codereview.stackexchange.com/questions/13007/c-linked-list-implementation

【问题讨论】:

  • 如果你在调试器中运行它,你会知道是哪一行导致了错误。
  • @CodeKingPlusPlus:emp->name = "Dan";????不应该是strcpy(emp->name, "Dan")吗?
  • @Oli Charlesworth: Ted?保证"Exercise in Poor Taste" ;)
  • @Oli Charlesworth 对 strcpy 的好建议...我永远不会完全习惯 C...
  • "不应该是 strcpy(emp->name, "Dan") 吗?" -- 仅当您打算写入未分配的内存时。

标签: c generics pointers linked-list malloc


【解决方案1】:

问题在于这个 accList_allocate() 和你对它的使用。

struct accList secondList;
accList_allocate(&secondList);

在原始 test2() 中,secondList 是堆栈上的内存。 &secondList 是指向该内存的指针。当您调用 accList_allocate() 时,指针的副本被传递,指向堆栈内存。然后 malloc() 返回一块内存并将其分配给指针的副本,而不是原始的 secondList。

返回后,secondList 仍指向堆栈上未初始化的内存,因此对 appendToEnd() 的调用失败。

除了 secondList 恰好没有垃圾之外,答案也是如此。可能是偶然的,也可能是编译器的设计造成的。无论哪种方式,你都不应该依赖它。

要么:

struct accList *secondList = NULL;

accList_allocate(&secondList);

并更改 accList_allocate()

accList_allocate(struct accList **theList) {
    *theList = Malloc(sizeof(struct accList));
    (*theList)->head = NULL;
    (*theList)->tail = NULL;
    (*theList)->size = 0;
}

struct accList secondList;

accList_initialise(secondList);

将 accList_allocate() 更改为 accList_initialise() 因为它不分配

accList_initialise(struct accList *theList) {
    theList->head = NULL;
    theList->tail = NULL;
    theList->size = 0;
}

【讨论】:

    【解决方案2】:

    我认为你的问题是这样的:

    1. 您已在原始test2 函数的堆栈上分配了secondList
    2. 堆栈内存可能是脏的,所以secondList需要初始化
    3. 您的accList_allocate 函数接受一个指向列表的指针,然后用Malloc 调用覆盖它。这意味着您传入的指针从未初始化。
    4. test2 尝试运行时,它遇到了错误的指针(因为内存未初始化)。

    当您在main 中分配它时它起作用的原因是您的 C 编译器可能在程序启动时将堆栈归零。当main 在堆栈上分配一个变量时,该分配是持久的(直到程序结束),因此当您在main 中分配它时,secondList 实际上并且意外地正确初始化了。

    您当前的accList_allocate 实际上并没有初始化传入的指针,您的其余代码将永远不会看到它使用Malloc 分配的指针。为了解决您的问题,我将创建一个新函数:accList_initialize,其唯一工作是初始化列表:

    void accList_initialize(struct accList* theList)
    {
        // NO malloc
       theList->head = NULL;
       theList->tail = NULL;
       theList->size = 0;
    }
    

    在原来的 test2 函数中使用它,而不是 accList_allocate。如果您真的想在堆上分配列表,那么您应该这样做(而不是将它与分配在堆栈上的结构混合)。让accList_allocate 返回一个指向已分配结构的指针:

    struct accList* accList_allocate(void)
    {
       struct accList* theList = Malloc( sizeof(struct accList) );
       accList_initialize(theList);
       return theList;
    }
    

    【讨论】:

    • 验证这是否正确的一种方法是在将数据结构分配到堆栈之后,但在调用accList_allocate之前打印出来。使用 printf("size=%lu head=%p tail=%p\n", secondList->size, secondList->head, secondList->tail); 之类的东西。
    【解决方案3】:

    根据原代码,我在这里看到的两件事是错误的,在上面的问题中,

    您看到的是未定义的行为,由此产生的总线错误消息是您将字符串文字分配给变量时,实际上您应该使用 strcpy 函数,您已经编辑了您的相应的原始代码......将来要记住的事情:)

    Malloc这个词的使用会引起混乱,尤其是在同行评审中,审稿人会脑袋放屁,说“哇,这是什么,不应该是malloc吗?”并且很可能会提高它。 (基本上,不要调用与 C 标准库函数具有相似名称的自定义函数)

    您没有检查NULL,如果您的增强版Malloc 失败,那么emp 将是NULL!无论多么琐碎或您的想法是“啊,平台上有大量内存,4GB RAM 没问题,不会费心检查 NULL”,请务必检查它

    查看this question posted elsewhere,了解什么是总线错误。

    编辑: 使用链表结构,如何调用函数中的参数对于理解它至关重要。注意&的用法,意思是获取指向链表结构的变量的地址通过引用传递,而不是按值传递,这是变量的副本。同样的规则也普遍适用于指针的使用:)

    您的问题的第一个代码中的参数有点不合适,如果您在参数列表中使用双指针,那么是的,使用 &secondList 会起作用。

    【讨论】:

    • 我刚刚发布了 Malloc 的功能,它只是检查 NULL。阅读赏金下方的文字并将您的答案转发给该文字...
    • 不,这是为了让我可以拥有通用链表并进行练习。当我释放列表中的节点时,下一步将释放列表中的“对象类型”。我打算使用指向对象释放函数的函数指针。
    【解决方案4】:

    这可能取决于您的员工结构是如何设计的,但您应该注意

    strcpy(emp->name, "Dan");
    

    emp->name = "Dan";
    

    功能不同。特别是,后者可能是总线错误的来源,因为您通常不能以这种方式写入字符串文字。特别是如果您的代码有类似的东西

    name = "无"

    或类似的。

    编辑:好的,所以对于员工结构的设计,问题是这样的:

    您不能分配给数组。 C 标准包括一系列可修改的左值,而数组不是其中之一。

    char name[20];
    name = "JAMES" //illegal
    

    strcpy 很好 - 它只是转到由 name[0] 取消引用的内存地址,并将“JAMES\0”复制到那里的内存中,一次一个字节。

    【讨论】:

    • 我的答案使用 strcpy。我正在寻找为什么我的答案可以解决我的问题。我相信它与指针和分配有关。 “我的答案”作为文字堆栈溢出答案发布。
    • 我正试图回答这个问题,但您忽略了关键部分(员工结构的设计)。您的回答还暗示修复来自重新安排内存分配的范围(“当我将 secondList 的声明和分配放在 main() 并将其余部分放在它工作的函数中”)。
    • 好的,你的结构格式让问题更清楚了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多