《目录》
- 前言
- 语句表达式
- typeof
- container_of 宏
- case 范围的扩展
- 扩展数组的初始化
- 扩展构造函数和析构函数
- 如何确认 Malloc 的调用次数
- 扩展数据的可移植性
- 可变参数宏
- 扩展无返回值
- 扩展内建函数
- 内建函数:__builtin_constant_p()
- 内建函数:__builtin_expect()
- format
- 零长度数组
- 属性声明:__attribute__
- 属性声明:section
- __init 宏
- Linux内核通用链表
GNU C 语法 是阅读 Linux 源码的必经之路,也是成为 C 语言大神较关键的一步。
GNU C 语法 是在 C 标准上的扩展。
比如,C 中的数据类型,运算符的优先级和结合性 等, 都是 C 标准之一。
C 标准到目前为止,共更新 4 次:
- K&R C:《C 程序设计语言》
- ANSI C:《ANSI C 官方文档》
- C99:《C 语言那些事儿》/《C 语言国际标准 C99》
- C11
主流的编译器,基本都会支持最新的 C 标准。
不过,不同编译器对 C 标准的支持也分为俩大派。
- VC 派
- GCC 派
我们的 GNU C 语法,是 GCC 派,GNU C 语法就是 GCC 在 C 标准上的扩展。
Linux 内核源码实例:
表达式:如, i += 1
语句:如, i += 1;
语句表达式:如,({ i +=1; j += 1; }); 语句表达式的值是最后一个表达式,掌握这个就可以作为循环的结束条件。
举个例子:
// ({ ... }) : 语句表达式
#include
int main( void )
{
int sum = 0;
sum = ({
int s = 0;
for( int i = 0; i < 10; i ++ )
s += i;
s; // sum = s, s; 是最后一个表达式
}) ;
// ({...}) 很灵活,和在直接写差不多,不过要特别注意最后一个表达式的写法,另外,goto 也可以放进去。
printf("%d", sum);
return 0;
}
再看看上面的图片,发现都是使用的 ({ .. }) 代替宏。
如,取俩个数中的最大值:
#define MAX(x, y) ((x)>(y) ? (x) : (y))
Linux 是这样写的:
#define MAX( type, x, y ) ({ \
type _x = (x); \
type _y = (y); \
_x > _y ? _x : _y; \
})
因为第一种不安全,如比较 MAX(i++, j++),这是通过第一种宏定义展开 i, j 会自增俩次。
第二种的最后一句是 _x > _y ? _x : _y; \,整个 MAX(type, x, y) 最后返回的值就是最后一句的值。
调用 MAX() 函数时,还得把类型写上。
因此,GNU C 扩展了 typeof 关键字。
sizeof :获取 变量、类型 的大小。
typeof :获取 变量、表达式 的类型。
因此,就可以修改上面的 MAX() 函数。
#define MAX( x, y ) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
_x > _y ? _x : _y; \
})
Linux 内核中的代码是这样:
#define MAX( x, y ) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void)(&_x == &_y); \
_x > _y ? _x : _y; \
})
(void)(&_x == &_y) :就是判断俩个数的类型,如果俩个数的类型不同编译器就会发出警告,说不同类型的指针不能比较。
补充一些基本的用法:
主要是为了获取结构体的首地址。
看看,我仿写的,那时候我还不知道原来这就是 Linux 的 offsetof 宏:
// 获取结构体的偏移量
#define offsetof(s,m) (((size_t)&(((s*)0)->f))
typedef struct stu
{
char c1;
long i;
char c2;
double f;
}ST;
// 调用宏:
printf("offset of : %d\n",offsetof(ST, f)); // f 是最后一个结构体成员
分析一下 offsetof 宏:
- 结构体变量名,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&
- (size_t) & (((s*)0)->f)
- 展开是,
- (size_t) & (((ST*)0)->f)
- 分解的意思是:
- (ST*)0 表示将数字 0 强行转为类型为结构体指针类型(ST*),这样 0 就是结构体的首地址 了。
- (ST*)0)->f 表示 调用成员变量 f。
- &(((ST*)0)->f) 表示 取出指针变量 f 的内存地址。
- 因为结构体的首地址为 0,那么成员变量的地址就是 0 的偏移地址。
- 所以 &(((ST*)0)->f) 这个值就是变量 f 的偏移值。
- 最后用 (size_t) 将这个偏移值再一次强转为 size_t 类 型。
container_of 宏就是获取结构体的首地址,与 offsetof 宏是一个组合。
看代码:
#include
int main( )
{
int i = 4;
switch(i)
{
case 0:
puts("0");
break;
case 2 ... 8: // Look !
puts("2~8");
break;
case 9:
puts("9");
break;
default:
puts("default");
break;
}
return 0;
}
看代码:
// 数组赋值的写法
int a[ 10 ] = { [9] = 233 }; // 单个赋值
int b[ 10 ] = { [0 ... 6] = 2333, [7 ... 9] = 100 }; // 范围赋值
使用方法:
__attribute__((constructor)) int init_func(void);
__attribute__((destructor)) int exit_func(void);
举例:
#include
// 构造函数,运行在 main( ) 之前
__attribute__((constructor)) int init_func(void)
{
puts(__func__);
}
// 析构函数,运行在 main( ) 之后
__attribute__((destructor)) int exit_func(void)
{
puts(__func__);
}
int main( )
{
puts(__func__);
return 0;
}
如题,我先把代码贴上。
#include
#include
#include
#include
#include
#include
#include
void *malloc( size_t size )
{
char buf[ 32 ];
static void *( *real_malloc )(size_t) = NULL;
if( real_malloc == NULL ) {
real_malloc = dlsym( RTLD_NEXT, "malloc");
}
sprintf(buf, "malloc called, size = %zu\n", size);
write( 2, buf, strlen(buf) );
return real_malloc(size);
}
int main( )
{
puts("\n本程序调用的 malloc 次数及大小(上面的是系统调用的):");
malloc( 1024 );
return 0;
}
运行后就发现,天哪,原理在运行 main() 函数之前,系统干了这么多事情。
这是因为执行 main() 之前,还有一些暗箱操作。
这些事情都是 CRT 运行库做的。
C 语言并没有严格规定 short、int、long 的长度,只做了宽泛的限制:
总结起来,它们的长度(所占字节数)关系为:
2 ≤ short ≤ int ≤ long
这就意味着,short 并不一定真的 “短”,long 也并不一定真的 “长”,它们有可能和 int 占用相同的字节数。
在 16 位环境下,short 的长度为 2 个字节,int 也为 2 个字节,long 为 4 个字节。16 位环境多用于单片机和低级嵌入式系统,在PC和服务器上已经见不到了。
对于 32 位的 Windows、Linux 和 Mac OS,short 的长度为 2 个字节,int 为 4 个字节,long 也为 4 个字节。PC和服务器上的 32 位系统占有率也在慢慢下降,嵌入式系统使用 32 位越来越多。
在 64 位环境下,不同的操作系统会有不同的结果,如下所示:
目前我们使用较多的PC系统为 Win XP、Win 7、Win 8、Win 10、Mac OS、Linux,在这些系统中,short 和 int 的长度都是固定的,分别为 2 和 4,大家可以放心使用,只有 long 的长度在 Win64 和类 Unix 系统下会有所不同,使用时要注意移植性。
但如果我们一定要用某种特定长度的数据类型咋办 ??
GNU C 扩展了 mode 属性声明,Linux 内核中运用的也极其广泛。
使用 mode 声明的变量,会固定长度。
使用示例:
• typedef int a __attribute__((mode(QI)));
• typedef unsigned int a __attribute__((mode(QI)));
QI 是 mode 的属性参数,代表 8 个字节。
- QI : 8 bits,int 类型
- HI : 16 bits,int 类型
- SI : 32 bits,int 类型
- DI : 64 bits,int 类型
- SF : 32 bits,float 类型
- DF : 64 bits,float 类型
#include
typedef int a8 __attribute__( (mode( QI ) ) );
typedef int a16 __attribute__( (mode( HI ) ) );
typedef int a32 __attribute__( (mode( SI ) ) );
typedef int a64 __attribute__( (mode( DI ) ) );
int main( )
{
a8 k;
printf("k = %d, a8 = %d\n", sizeof(k), sizeof(a8) );
return 0;
}
俩种定义方法:
C99 :
#define debug(fmt, ...) printf( fmt, __VA_ARGS__ )
GNU C:
#define Debug(fmt, args...) printf( fmt, args )
俩者的作用是一样的,都可以接受参数列表。
自己按照 printf() 函数的格式试一下,如 debug("%s", "hello")。
但俩者都不能传单个参数,如 debug("hello"),因为如果只有一个参数,那 宏里的 fmt 后面的逗号就当了参数的一部分,会导致语法错误,如 debug("hello") 经过宏展开变成了 printf("hello",) 。
使用 ## ,可消除后面的逗号。
C99 :
#define debug(fmt, ...) printf( fmt, ##__VA_ARGS__ )
GNU C:
#define Debug(fmt, args...) printf( fmt, args )
Linux 内核中的可变参数宏:
noreturn : 为函数声明,无返回值。
使用示例:
如果不加 __attribute__((noreturn)); ,运行 gcc -Wall 当前源文件.c
因为加了 -Wall , 所以会有警告信息。
现在加 __attribute__((noreturn)); 警告信息也不会有了。
内建,即编译器自带的,不需要引入头文件,如 int、main 、struct 都是内建的。
内建函数也是如此,函数是编译器自带的,不需要引入头文件,主要用于性能优化。
以下的 C 标准库函数,其实都是内建函数,不需要引入头文件即可使用。
内存相关函数:memcpy 、memset、memcmp
数学函数:log、cos、abs、exp、
字符串处理函数:strcat、 strcmp、strcpy 、 strlen
打印函数:printf、scanf、putchar、puts
条件是,加前缀 __builtin_。
如:__builtin_puts( )、__builtin_memcpy( ),有一些出于安全、性能方面考虑,参数个数和 C 标准库函数不太一样,因为 C 标准库函数的函数不安全,如 strcpy() 、memcpy( )、scanf( )、gets( )。
还有俩个特别有用的:
__builtin_return_address(LEVEL)
__builtin_frame_address(LEVEL)
__builtin_constant_p( n ):用来判断 变量n 是 变量 还是 常量。
__builtin_expect(表达式,n):表示 表达式 的值为 n 的可能性很大,而这个函数的返回值就是表达式的值。
format 是一个用于变参函数的声明。
不了解 变参 吧,printf() 函数就是一个变参函数。
举个例子,现在我自己实现一个变参函数 print() ,功能是打印传进来的所有参数 。
函数原型:void print( ){ ... }
变参函数的原型:void print( int cnt , ...){ ... }
运行下面的代码,可以打印出 1 ~ n。
#include
// 功能是打印传进来的所有参数
int print( int cnt, ... )
{
int *args; // 这里应该写 char *args,但我编译器不支持
args = (char *)&cnt + 4; // 一般都是字符型,如果是 int * ,cnt + 1
for( int i = 0; i < cnt; i ++ )
printf("%d ", *args++);
}
int main( )
{
int n;
scanf("%d", &n);
print(n, 1, 2, 3, 4, 5);
return 0;
}
我在 TCC 上运行成功,GCC 不知道为什么出错了。
为了代码的通用性、可读性,一些地方要改为 C 定义好的 宏。
另外,添加头文件 stdarg.h ,其余是一样的。
那 format(缩写 fmt) 是干嘛的,format 就是用来代替 cnt 的。
零长度数组,是说某个结构体里有一个长度为 零 的数组。
因为这个数组长度是 零,所以不占空间,只是表示一个地址而已啦。
typedef struct _a{
int len;
char a[0]; // 零长度数组
}a;
使用 sizeof(a) 得到结构体的长度就是 4。
typedef struct _a{
int len;
char a[0]; // 如果使用 char *a,这样就占空间了
}a;
int main( )
{
__builtin_printf("%d\n", sizeof(a) );
a* A;
A = ( a* )__builtin_malloc ( sizeof( a ) + 10 );
A -> len = 20;
__builtin_printf("%d\n", sizeof(A) );
// 使用零长度数组
__builtin_strcpy( A->a, "hello, world!");
__builtin_puts( A->a );
free( A );
return 0;
}
零长度数组应用在 Linux内核 中,一般是 USB 驱动(linux/usb.h)。
鼠标、键盘传输的时候不需要,当视频同步传输时却需要,因此使用 char a[0] 代替 char *,因为 char a[0] 不用时,便不占用空间。
__attribute__ 可以告诉编译器此 变量/函数 需要检查或优化。
section 可以指定 变量/函数 的存储空间。
使用格式:
int a __attribute__((section(".data")));
测试:
int a __attribute__((section(".data")));
int main( )
{
}
一些函数只想用于驱动,即就启动的时候用一下。
加了 __init 宏 后,启动后就会把这个函数释放的空间释放掉。
加了 __init 宏 的函数,就像一次性筷子,用一次就会扔掉。
一般先由 section 把一次性函数指定到某个段(.init.text) ,再使用 __init 宏 即可让内核释放。
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
// 初始化·宏
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
// 初始化·内联函数
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
#ifndef CONFIG_DEBUG_LIST
// 添加节点·内联函数
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
// 添加节点·宏
#else
extern void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#endif
// 头插,参数设计很为用户着想
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
// 尾插,参数设计很为用户着想
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
#ifndef CONFIG_DEBUG_LIST
// 删除,参数设计很为用户着想
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1; // 指向一个特殊的地址,如果操作也会出现段错误
entry->prev = LIST_POISON2; // 指向一个特殊的地址,如果操作也会出现段错误
}
#else
extern void __list_del_entry(struct list_head *entry);
extern void list_del(struct list_head *entry);
#endif
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) \
(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* 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_for_each_entry_reverse - iterate backwards 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_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_head within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))
/**
* list_for_each_entry_continue - continue iteration 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.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_next_entry(pos, member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @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.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @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.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; &pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member), \
n = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_prev_entry(n, member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_head within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_next_entry(pos, member)
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
// WRITE_ONCE(*pprev, next);
if (next)
next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void hlist_add_behind(struct hlist_node *n,
struct hlist_node *prev)
{
n->next = prev->next;
prev->next = n;
n->pprev = &prev->next;
if (n->next)
n->next->pprev = &n->next;
}
/* after that we'll appear to be on some hlist and hlist_del will work */
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}
//static inline bool hlist_fake(struct hlist_node *h)
//{
// return h->pprev == &h->next;
//}
/*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *new)
{
new->first = old->first;
if (new->first)
new->first->pprev = &new->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_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 hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(pos, member) \
for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(pos, member) \
for (; pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
#endif
调用示范:
#include
#include
#include"list.h"
struct device{
int num;
struct list_head node;
};
int main(void)
{
struct device *p;
struct device head;
struct list_head *pos,*q;
INIT_LIST_HEAD(&head.node);
for(int i=0;i<5;i++){
p = (struct device*)malloc(sizeof(struct device));
printf("input device num:");
scanf("%d",&p->num);
list_add(&(p->node),&(head.node));
}
puts("list_for_each:");
list_for_each(pos,&(head.node)){
p = list_entry(pos,struct device, node);
printf("num = %d\n",p->num);
}
//delete
list_for_each_safe(pos,q,&head.node){
p = list_entry(pos,struct device,node);
printf("freeing node: num = %d\n",p->num);
list_del(pos);
free(p);
}
return 0;
}
【更新ing...】