FreeRTOS---进阶知识4---通用链表
在之前的链表操作中可以看到,在创建struct person结构体时,其结构体成员和指针是放在同一个结构体中:
struct person {char *name;int age;struct person *next; // 数据和指针耦合
};下面以添加链表操作为例,介绍上面代码的局限性:
struct person{char *name;int age;struct person *next;/*指向下一个人*/
};struct list{char *name;/*列表名称*/struct person head;/*指向某个人*/
};void InitList(struct list *pList,char *name)
{pList ->name = name ;pList ->head.next = NULL ;
}void AddItemToList(struct list *pList,struct person *new_person)
{struct person *last = &pList->head;while (last->next){last = last->next;}last->next = new_person;new_person->next = NULL;
}上面的代码本身是改进过链表的定义内容,但是如果需要将其他类型的结构体添加进链表时,就会出现代码冗余的问题。
比如,需要重新定义结构体 struct bird{ },将 struct bird{ }也添加进链表,那么就需要重新写个代码 Void AddItemToList(struct list *pList,struct bird *new_bird),当有多个结构体类型时,都要写一个新的函数。这显然是有问题的。
下面,将介绍通用链表操作,也就是说不管结构体的类型如何,只需要一个函数Void AddItemToList()即可。
具体代码如下:
struct node_t{struct node_t *next;
}struct person{char *name;int age;struct node_t node;
};struct list{char *name;/*列表名称*/struct node_t head;
};void InitList(struct list *pList,char *name)
{pList ->name = name ;pList ->head.next = NULL ;
}void AddItemToList(struct list *pList,struct node_t *new_node)
{struct node_t *last = &pList->head;while (last->next){last = last->next;}last->next = new_node;new_node->next = NULL;
}
实际内存结构
两个person实例:
person1 实例 +-------------+ | name:"张三" | ← 数据 +-------------+ | age:18 | ← 数据 +-------------+ | node.next | → 指向person2的node ← 连接环 +-------------+person2 实例 +-------------+ | name:"李四" | ← 数据 +-------------+ | age:20 | ← 数据 +-------------+ | node.next | → NULL ← 连接环 +-------------+
链表连接关系:
list.head +----------+ | next | → 指向person1的node +----------+person1.node +----------+ | next | → 指向person2的node +----------+person2.node +----------+ | next | → NULL +----------+

如何从连接环找到数据?
问题:
我们知道node的地址,如何找到包含它的person?
解决方案:计算偏移量
// 方法1:使用offsetof宏
struct person* node_to_person(struct node_t *node)
{return (struct person*)((char*)node - offsetof(struct person, node));
}// 方法2:假设node在person中的位置
struct person* node_to_person(struct node_t *node)
{return (struct person*)((char*)node - 8); // 假设name和age共占8字节
}完整使用示例
1. 创建数据节点
struct person p1 = {"张三", 18, {NULL}};
struct person p2 = {"李四", 20, {NULL}};2. 初始化链表
struct list my_list;
InitList(&my_list, "班级列表");3. 添加节点
AddItemToList(&my_list, &p1.node); // 传递node的地址
AddItemToList(&my_list, &p2.node); // 传递node的地址4. 遍历链表
void PrintList(struct list *pList)
{// 从头节点的第一个连接环开始struct node_t *current = pList->head.next;while (current != NULL) {// 从连接环找到包含它的personstruct person *p = (struct person*)((char*)current - 8);printf("姓名: %s, 年龄: %d\n", p->name, p->age);// 移动到下一个连接环current = current->next;}
}