我有一个通用链接列表,其中包含 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;
}
你的问题是这样的:
- 您已在原始
test2
函数中的堆栈上分配了secondList
。 - 堆栈内存可能很脏,所以
secondList
需要初始化 accList_allocate
函数获取指向列表的指针,但随后使用Malloc
调用覆盖它。 这意味着您传入的指针永远不会初始化。- 当
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"复制到那里的内存中,一次一个字节。