list_head: 如果从第二个元素开始解析则得到垃圾
list_head: get garbage if start parsing from second element
我在我的 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: %s\n", retreport->report);
}
printf("\n");
list_for_each_entry(retreport, reports.next, list){
printf("============> Next retreport: %s\n", 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()
将通过头部。并且头部没有附加到条目上。因此,当您尝试引用头列表中的条目时,您将得到垃圾
解决方法:从链表头部开始循环,跳过第一个元素
列表实现实际上创建了一个环。列表头是一个虚拟元素,它将 next
指向第一个元素,将 prev
指向最后一个元素。 (最初两者都指向列表头本身。)在尾部添加一个元素实际上实现为添加它"before the list head"。在这个环上循环时,头部由指向它的单独指针标记。没有其他方法可以将它与列表的其他元素区分开来。
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_entry
和 list_next_entry
return 指向用户定义的结构的指针,该结构应该通过使用宏 container_of
包含 struct list_head
.
如果您将 reports.next
而不是 &reports
传递给 list_for_each_entry()
,它将把它作为虚拟列表头元素,并将环中的所有其他元素视为真正的列表条目。
您的代码为尾部元素后面的元素打印垃圾,因为这是一个纯 struct list_head
,未嵌入 struct struct_report
,因此宏 list_next_entry
return 是指向 main()
中 struct list_head reports
之前的内存的指针,这是未定义的行为。
如果你的程序没有崩溃,你会在 elt4
之后得到同样的垃圾,如果你通过例如reports.next->next
。在这种情况下,我希望输出如下:
============> Next retreport: elt3
============> Next retreport: elt4
============> Next retreport: <garbage>
============> Next retreport: elt1
虽然同一类型 - list_head
- 用于两者:
- 列表头,
- 列表元素,
它们不可互换。如果某些宏需要 list head 作为参数,您需要提供 exactly 指向 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: %s\n", retreport->report);
}
我在我的 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: %s\n", retreport->report);
}
printf("\n");
list_for_each_entry(retreport, reports.next, list){
printf("============> Next retreport: %s\n", 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()
将通过头部。并且头部没有附加到条目上。因此,当您尝试引用头列表中的条目时,您将得到垃圾
解决方法:从链表头部开始循环,跳过第一个元素
列表实现实际上创建了一个环。列表头是一个虚拟元素,它将 next
指向第一个元素,将 prev
指向最后一个元素。 (最初两者都指向列表头本身。)在尾部添加一个元素实际上实现为添加它"before the list head"。在这个环上循环时,头部由指向它的单独指针标记。没有其他方法可以将它与列表的其他元素区分开来。
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_entry
和 list_next_entry
return 指向用户定义的结构的指针,该结构应该通过使用宏 container_of
包含 struct list_head
.
如果您将 reports.next
而不是 &reports
传递给 list_for_each_entry()
,它将把它作为虚拟列表头元素,并将环中的所有其他元素视为真正的列表条目。
您的代码为尾部元素后面的元素打印垃圾,因为这是一个纯 struct list_head
,未嵌入 struct struct_report
,因此宏 list_next_entry
return 是指向 main()
中 struct list_head reports
之前的内存的指针,这是未定义的行为。
如果你的程序没有崩溃,你会在 elt4
之后得到同样的垃圾,如果你通过例如reports.next->next
。在这种情况下,我希望输出如下:
============> Next retreport: elt3
============> Next retreport: elt4
============> Next retreport: <garbage>
============> Next retreport: elt1
虽然同一类型 - list_head
- 用于两者:
- 列表头,
- 列表元素,
它们不可互换。如果某些宏需要 list head 作为参数,您需要提供 exactly 指向 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: %s\n", retreport->report);
}