双向有头链表的创建
Dou_node *create_doulink()
{
Dou_node *pnode = malloc(sizeof(Dou_node));
if (NULL == pnode)
{
printf("fail malloc");
return NULL;
}
pnode->ppre = NULL;
pnode->pnext = NULL;
return pnode;
}
链表是否为空的判断
int is_empty_doulink(Dou_node *phead)
{
if (NULL == phead->pnext)
{
return 1;
}
return 0;
}
双向链表的头插
int insert_head_doulink(Dou_node *phead, Data_type data)
{
Dou_node *pinsert = malloc(sizeof(Dou_node));
if (NULL == pinsert)
{
printf("fail malloc");
return -1;
}
pinsert->data = data;
pinsert->ppre = NULL;
pinsert->pnext = NULL;
if (is_empty_doulink(phead))
{
phead->pnext = pinsert;
pinsert->ppre = phead;
}
else
{
pinsert->pnext = phead->pnext;
phead->pnext->ppre = pinsert;
phead->pnext = pinsert;
pinsert->ppre = phead;
}
return 0;
}
双向链表的尾插
int insert_tail_doulink(Dou_node *phead, Data_type data)
{
Dou_node *pinsert = malloc(sizeof(Dou_node));
if (NULL == pinsert)
{
printf("fail malloc\n");
return -1;
}
pinsert->data = data;
pinsert->pnext = NULL;
pinsert->ppre = NULL;
Dou_node *p = phead;
while (p->pnext != NULL)
{
p = p->pnext;
}
p->pnext = pinsert;
pinsert->ppre = p;
return 0;
}
双向链表的遍历
void doulink_for_each(Dou_node *phead, int dir)
{
if (is_empty_doulink(phead))
{
return ;
}
Dou_node *p = phead->pnext;
if (dir)
{
while (p)
{
printf("%d %s %d\n", p->data.id, p->data.name, p->data.score);
p = p->pnext;
}
}
else
{
while (p->pnext != NULL)
{
p = p->pnext;
}
while (p->ppre != NULL)
{
printf("%d %s %d\n", p->data.id, p->data.name, p->data.score);
p = p->ppre;
}
}
printf("\n");
}
双向链表的删除
int delete_head_doulink(Dou_node *phead) //头删
{
if (is_empty_doulink(phead))
{
return 0;
}
Dou_node *pdel = phead->pnext;
phead->pnext = pdel->pnext;
if (pdel->pnext != NULL)
{
pdel->pnext->ppre = phead;
}
free(pdel);
return 1;
}
int delete_tail_doulink(Dou_node *phead) //尾删
{
if (is_empty_doulink(phead))
{
return 0;
}
Dou_node *pdel = phead->pnext;
while (pdel->pnext != NULL)
{
pdel = pdel->pnext;
}
pdel->ppre->pnext = NULL;
free(pdel);
return 1;
}
int delete_point_node(Dou_node *phead, char *name) //指定位置的删除
{
if (is_empty_doulink(phead))
{
return 0;
}
Dou_node *pdel = find_doulink_by_name(phead, name);
if (NULL == pdel)
{
return 0;
}
if (NULL == pdel->pnext)
{
delete_tail_doulink(phead);
}
else
{
pdel->ppre->pnext = pdel->pnext;
pdel->pnext->ppre = pdel->ppre;
free(pdel);
}
return 1;
}
void destroy_doulink(Dou_node **pphead) //双向链表的销毁
{
while (!is_empty_doulink(*pphead))
{
delete_head_doulink(*pphead);
}
free(*pphead);
*pphead = NULL;
}
双向链表数据的查找
Dou_node *find_doulink_by_name(Dou_node *phead, char *name)
{
Dou_node *p = phead->pnext;
while (p)
{
if (0 == strcmp(p->data.name, name))
{
return p;
}
p = p->pnext;
}
return NULL;
}
1.定位段错误
2.用来调试逻辑错误
-g:允许gdb经行调式
gdb可执行程序
一些常用指令
l (查看源代码)
b 断点
r 运行程序
n 继续执行下一段代码
s 进入函数里面
p 变量名 (查看该变量)
q 退出函数
where 查看段错误的栈信息
普通链表:结构简单,易于实现,适用于大多数用户级应用,但在性能和内存管理上有所限制,特别是对于需要频繁访问和操作大量数据的情况。
内核链表:本质是一个双向有头的循环链表。:在内核环境中表现优越,通过优化操作、节省内存、与内存管理机制结合,提供更高效的链表实现。适合于操作系统内部或高性能计算环境中
offsetof
宏用于计算结构体成员相对于结构体起始位置的字节偏移量
container_of
宏用于根据结构体成员的地址(通常是指向某个成员的指针)反向推算出包含该成员的整个结构体对象的地址。这个宏常用于链表遍历、内核数据结构中的容器结构体操作等场景。
指针函数是返回指针的函数。也就是说,这种函数的返回值是指针类型,它可以指向某种数据类型。
指针函数的返回类型是一个指针,表示该函数返回一个指向某个数据类型的指针。
int* func(); // 定义一个返回 int 指针的函数
#include
int* pointer_func() {
static int num = 10; // 使用 static 来保证变量 num 在函数外部可以访问
return # // 返回 num 的地址
}
int main() {
int *ptr = pointer_func();
printf("Value: %d\n", *ptr); // 输出:10
return 0;
}
在这个例子中,pointer_func
是一个指针函数,返回一个指向 int
类型的指针。在 main
函数中,我们调用 pointer_func
并获取返回的指针,最终通过解引用 *ptr
来访问值。
static
声明(否则局部变量会在函数返回后被销毁)。函数指针是指向函数的指针,允许通过指针间接调用函数。这是 C 语言中非常强大的特性,广泛应用于回调函数、动态调用等场景。
函数指针的类型与其指向的函数的签名(返回类型和参数类型)一致。例如,指向返回 int
且接受两个 int
参数的函数的指针类型为:
int (*func_ptr)(int, int); // 定义一个函数指针,指向一个接收两个 int 参数并返回 int 的函数
#include
// 定义一个普通的函数
int add(int a, int b) {
return a + b;
}
int main() {
// 定义函数指针并将其指向 add 函数
int (*func_ptr)(int, int) = add;
// 使用函数指针调用函数
int result = func_ptr(2, 3); // 相当于调用 add(2, 3)
printf("Result: %d\n", result); // 输出:Result: 5
return 0;
}
指针数组是一个数组,每个元素都是一个指向某种类型的指针。指针数组的元素可以指向同一类型的数据。
指针数组的定义方式类似于普通数组,只不过数组的元素是指针类型
int* arr[5]; // 定义一个指向 int 的指针数组,包含 5 个元素
#include
int main() {
int a = 10, b = 20, c = 30;
// 定义一个指针数组
int* arr[3];
// 让数组的每个元素指向不同的变量
arr[0] = &a;
arr[1] = &b;
arr[2] = &c;
// 通过指针数组访问数据
printf("%d %d %d\n", *arr[0], *arr[1], *arr[2]); // 输出:10 20 30
return 0;
}
数组指针是一个指向数组的指针。与指针数组不同,数组指针指向一个整体的数组而不是数组中的单个元素。数组指针的类型是指向数组的指针。
数组指针的定义形式如下
int (*ptr)[5]; // ptr 是一个指向包含 5 个整数的数组的指针
#include
int main() {
int arr[3] = {1, 2, 3};
// 定义一个指向数组的指针
int (*ptr)[3] = &arr;
// 通过数组指针访问数组元素
printf("%d %d %d\n", (*ptr)[0], (*ptr)[1], (*ptr)[2]); // 输出:1 2 3
return 0;
}
[]
运算符来访问数组的元素。