C语言 list_head:如果从第二个元素开始解析,则获取垃圾



我在我的C项目中使用list_head结构来定义linked_list。在某些情况下,我需要从第二个元素解析列表,但在这种情况下,我得到了一个带有垃圾值的附加元素。我尝试在电脑上使用一个小程序来模拟相同的场景。我得到同样的问题:

#include<stdio.h>
#include<stdlib.h>
#include "list.h"
struct struct_report{
struct list_head list;
char *report;
};

//Add an element to the linked list
void add_report_to_list(struct list_head *reports, char *report) {
struct struct_report *report_strct;
report_strct = calloc(1, sizeof(struct struct_report));
list_add_tail(&report_strct->list, reports);
report_strct->report= strdup(report);
}
int main() {
struct struct_report *retreport;
LIST_HEAD(reports); //instantiate a struct list_head instance
add_report_to_list(&reports, "elt1");
add_report_to_list(&reports, "elt2");
add_report_to_list(&reports, "elt3");
add_report_to_list(&reports, "elt4");
list_for_each_entry(retreport, &reports, list){
printf("============> no next retreport: %sn", retreport->report);
}
printf("n");
list_for_each_entry(retreport, reports.next, list){
printf("============> Next retreport: %sn", retreport->report);
}
return 1;
} 

list.h与Linux相同:https://github.com/torvalds/linux/blob/master/include/linux/list.h

我得到以下跟踪作为执行结果:

============> no next retreport: elt1
============> no next retreport: elt2
============> no next retreport: elt3
============> no next retreport: elt4
============> Next retreport: elt2
============> Next retreport: elt3
============> Next retreport: elt4
============> Next retreport: 

很明显,如果我从第一个元素开始正常解析,我没有任何问题。但是,如果我从列表中的第二个元素开始,我会得到一个具有奇怪值的元素,例如垃圾。

有一些解释为什么我会得到一个额外的元素?我如何修复它以解析直到 elt4?

如果您从列表的第一个元素(而不是从头部(开始,则list_for_each_entry()将在同一列表对象中停止,因为它是一个循环列表。

所以list_for_each_entry()会穿过头部。并且头部未连接到入口。因此,当您尝试引用 head 列表中的条目时,您将获得垃圾

解决方案:从列表的头部开始循环并跳过第一个元素

列表实现实际上创建了一个环。列表头是一个虚拟元素,它next指向第一个元素,prev指向最后一个元素。(最初两者都指向列表头本身。 在尾部添加一个元素实际上被实现为"在列表头之前"添加它。当环过这个环时,头部由指向它的单独指针标记。没有其他方法可以将其与列表中的其他元素区分开来。

list_for_each_entry中的for循环与作为循环条件的head指针进行比较,因此当它再次到达作为列表头提供的对象时,它将停止。

/**
* list_for_each_entry  -   iterate over list of given type
* @pos:    the type * to use as a loop cursor.
* @head:   the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member)              
for (pos = list_first_entry(head, typeof(*pos), member);    
&pos->member != (head);                    
pos = list_next_entry(pos, member))

list_first_entrylist_next_entry都返回指向用户定义结构的指针,该结构应通过使用宏container_of包含struct list_head

如果您传递reports.next而不是&reportslist_for_each_entry(),它将将其作为虚拟列表头元素,并将环中的所有其他元素视为真正的列表条目。

你的代码为tail元素后面的元素打印垃圾,因为这是一个没有嵌入struct struct_report的纯struct list_head,所以宏list_next_entrymain()struct list_head reports之前返回一个指向内存的指针,这是未定义的行为。

如果你的程序没有崩溃,你会在elt4后得到同样的垃圾,如果你会通过,例如reports.next->next.在这种情况下,我希望输出如下:

============> Next retreport: elt3
============> Next retreport: elt4
============> Next retreport: <garbage>
============> Next retreport: elt1

虽然相同的类型 -list_head- 用于两者:

  • 列表头,
  • 列表元素,

它们不可互换。如果某个宏需要列表头作为参数,则需要提供指向列表的确切指针,而不是指向列表元素的指针。

list_for_each_entry接受指向列表头的指针作为第二个参数,因此不应将指针传递给元素。

对于迭代列表时跳过第一个元素,宏

  • list_for_each_entry_from
  • list_for_each_entry_continue

可以使用。

这两个宏都采用与list_for_each_entry宏相同的参数,但它们都采用光标的初始值(第一个参数(:

  • list_for_each_entry_from光标指向的元素开始迭代,
  • list_for_each_entry_continue光标指向的元素之后开始迭代。

因此,通过跳过第一个元素来迭代列表可以如下:

// Set cursor to the first element in the list
retreport = list_first_entry(reports, typeof(*retreport), list);
// Iterate starting after the cursor
list_for_each_entry_continue(retreport, reports, list){
printf("============> Next retreport: %sn", retreport->report);
}

相关内容

  • 没有找到相关文章

最新更新