今天在项目中发现有一个很经典的链表操作,实现了结构中的成员与链表记录指针分离(C实现面象对象),而且整个链表的相关操作都是采用宏定义。
读懂内部操作查询了以下知识:
1、 单行宏定义的用法:主要有以下三种用法.
1) 前加##或后加##,将标记作为一个合法的标识符的一部分.注意,不是字符串.多用于多行的宏定义中.例如:
#define A(x) T_##x
则 int A(1) = 10; //等效于int T_1 = 10;
#define A(x) Tx##__
则 int A(1) = 10; //等效于int T1__ = 10;
2) 前加#@,将标记转换为相应的字符,注意:仅对单一标记转换有效(理解有误?)
#define B(x) #@x
则B(a)即’a’,B(1)即’1’.但B(abc)却不甚有效.
3) 前加#,将标记转换为字符串.
#define C(x) #x
则C(1+1) 即 ”1+1”.
在#define中,标准只定义了#和##两种操作。#用来把参数转换成字符串,##则用来连接前后两个参数,把它们变成一个字符串。
#include <stdio.h>
#define paster( n ) printf( "token " #n" = %d\n ", token##n )
int main()
{
int token9=10;
paster(9);
return 0;
}
输出为[leshy@leshy src]$ ./a.out token 9 = 10
2、0地址(让我重新认识了0地址)
把0作为结构的地址,(&((type *)0)->member)为以0为地址的结构,结构中元素member的地址。
换句话说,求出了结构type中元素member的偏移值。
&操作如果是对一个表达式,而不是一个标识符,会取消操作,而不是添加。
比如&*a,会直接把a的地址求出来,不会访问*a。
&a->member,会把访问a->member的操作取消,只会计算出a->member的地址。
ListNode gTestListHead;
typedef struct
{
int index;
ListNode node;
}TestStruct;
void test()
{
int32_t i =0;
ListNode *pListNode = NULL; //链表结点
TestStruct *pStructNode = NULL; //结构结点
ListNode *pListNodeTemp = NULL; //销毁时保存被销毁的下一个结点,方便找到下一个结点
INIT_LIST_HEAD(&gTestListHead);
for (i=0; i< 10; i++)
{
pStructNode = (TestStruct*)malloc(sizeof(TestStruct));
pStructNode->index = i * 10;
//将结点加入到链表尾部
list_add_tail(&(pStructNode->node), &gTestListHead);
}
//遍历链表
list_for_each(pListNode, &gTestListHead)
{
list_get_object(TestStruct, node, pListNode, pStructNode);
printf("结点 %d \r\n",pStructNode->index);
}
//销毁链表
list_for_each_safe(pListNode, pListNodeTemp, &gTestListHead)
{
pStructNode = list_entry(pListNode, TestStruct, node);
list_del(&(pStructNode->node));
i --;
free(pStructNode);
pStructNode = NULL;
}
INIT_LIST_HEAD(&gTestListHead);
printf("最后i的值 %d \r\n", i);
}