C 泛型链表



我有一个通用链接列表,其中包含 void* 类型的数据我正在尝试用结构类型员工填充我的列表,最终我也想破坏对象结构员工。

考虑这个通用的链接列表头文件(我已经用 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: %sn", ((struct employee*)secondList.head->data)->name);   //cast to type struct employee
      printf("Employee2: %sn", ((struct employee*)secondList.tail->data)->name);  
    }

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

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

问题是这个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;
}
我认为

你的问题是这样的:

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

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

您所看到的是未定义的行为,并且由此产生的是总线错误消息,因为您将字符串文字分配给变量,而实际上您应该使用 strcpy 函数,您已经相应地编辑了原始代码:)。

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

您没有检查NULL,如果您的增强版Malloc失败了怎么办,那么emp将被NULL!无论多么微不足道或您的想法是"啊,平台上有堆内存,4GB RAM没问题,不会费心检查NULL",始终检查它"

看看在其他地方发布的这个问题,以解释什么是总线错误。

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

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

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

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

emp->name = "Dan";

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

名称 ="无"

之类的。

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

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

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

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

相关内容

  • 没有找到相关文章

最新更新