我有一个通用链接列表,其中包含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
答案 0 :(得分: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;
}
OR
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 :(得分:1)
我认为你的问题是:
secondList
函数中的堆栈上分配了test2
。secondList
需要初始化accList_allocate
函数会获取指向列表的指针,但会使用Malloc
调用覆盖它。这意味着您传入的指针是从不初始化。test2
尝试运行时,它会遇到错误的指针(因为内存未初始化)。在main
中分配它时它起作用的原因是你的C编译器可能在程序启动时将堆栈归零。当main
在堆栈上分配变量时,该分配是持久的(直到程序结束),因此secondList
在main
中分配时,实际上并且意外地正确初始化了。{ / p>
您当前的accList_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;
}
答案 2 :(得分:0)
当我在main()中放置secondList的声明和分配,并将其余部分放在一个函数中时,它可以工作:
int main(int argc, char *argv[])
{
struct accList secondList;
accList_allocate(&secondList);
test2(&secondList);
return 0;
}
void test2(struct accList *secondList)
{
struct employee *emp = Malloc(sizeof(struct employee));
struct employee *emp2 = Malloc(sizeof(struct employee));
strcpy(emp->name, "Dan");
emp->wageRate = .5;
appendToEnd(emp, secondList);
strcpy(emp2->name, "Stan");
emp2->wageRate = .3;
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);
removeData(emp, secondList);
printf("Head: %s\n", ((struct employee*)secondList->head->data)->name);
}
struct employee
{
char name[20];
float wageRate;
}
以下是整个实施的链接。 https://codereview.stackexchange.com/questions/13007/c-linked-list-implementation
我觉得这与指针和分配有关。有人可以向我解释那部分吗?将问题中的代码与上面的代码进行比较。
谢谢!
答案 3 :(得分:0)
这可能取决于您的员工结构的设计方式,但您应该注意
strcpy(emp->name, "Dan");
和
emp->name = "Dan";
功能不同。特别是,后者可能是总线错误的来源,因为您通常无法以这种方式写入字符串文字。特别是如果您的代码具有类似
的代码name =“NONE”
等。
编辑:好的,所以对于员工结构的设计,问题是:
您无法分配数组。 C标准包括可修改的左值列表,而阵列不是其中之一。
char name[20];
name = "JAMES" //illegal
strcpy很好 - 它只是转到名称[0]取消引用的内存地址,并将“JAMES \ 0”复制到那里的内存中,一次一个字节。
答案 4 :(得分:0)
根据原始代码,我在这里看到错误的两件事,在上面的问题中,
您所看到的是未定义的行为,并且由此产生的是总线错误消息,因为您正在为变量分配字符串文字,而实际上您应该使用strcpy
函数,您已经原则上编辑了你的原始代码所以......将来要记住的事情:)
单词Malloc
的使用会引起混淆,特别是在同行评审中,评论者会有一个大脑屁并说“哇,这是什么,不应该是malloc?”并很可能提高它。 (基本上,不要调用具有与C标准库函数类似的声音名称的自定义函数)
您没有检查NULL
,如果Malloc
的加密版本失败,那么emp
将会NULL
怎么办?总是检查一下,无论多么微不足道或你的想法是“啊,这个平台上有大量内存,4GB RAM没问题,不会费心去检查NULL”
查看this question posted elsewhere来解释什么是总线错误。
编辑: 使用链表结构,如何调用函数中的参数对理解它至关重要。请注意& 的使用,意味着获取指向链接列表结构的变量的地址,通过引用传递,而不是传递值,这是变量的副本。同样的规则也适用于指针的使用:)
如果您在参数列表中使用了双指针,那么您在问题的第一个代码中的参数略有不同,那么使用&secondList
就可以了。