3

我有一个包含 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

4

4 回答 4

6

问题是这个 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;
}
于 2012-06-26T17:22:47.853 回答
1

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

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

分配它时它起作用的原因main是您的 C 编译器可能在程序启动时将堆栈归零。当main在堆栈上分配一个变量时,该分配是持久的(直到程序结束),所以secondList当你在main.

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

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

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

struct accList* accList_allocate(void)
{
   struct accList* theList = Malloc( sizeof(struct accList) );
   accList_initialize(theList);
   return theList;
}
于 2012-06-26T17:07:16.653 回答
0

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

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

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

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

看看这个发布在别处的问题来解释什么是总线错误。

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

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

于 2012-06-26T16:26:31.353 回答
0

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

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

emp->name = "Dan";

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

名称=“无”

之类的。

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

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

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

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

于 2012-06-26T16:18:31.900 回答