C-数据结构-双向环链-变长结构体的使用-面向对象的思想

/*
通用性比较强的双向环链
1.变长结构体的使用
2.面向对象的思想
*/

llist.h

#ifndef LLIST_H__
#define LLSIT_H__
#define LLIST_FORWARD	1
#definr LLIST_BACKWARD	2
typedef void llist_op(const void *);//回调函数
typedef int llist_cmp(const void *,const void *);struct llist_node_st
{struct llist_node_st *prev;struct llist_node_st *next;	char data[0];//变长结构体的使用(占位符)  必须放在结构体的下方
};
typedef struct llist_head
{int size;struct llist_node_st head;int (*insert)(struct llist_head *,const void *,int);//面向对象 把函数写入结构体之中  函数指针的实现  指针是指向某个函数的入口地址void *(*find)(struct llist_head *,const void *,llist_cmp *);int (*delete)(struct llist_head *,const void *,llist_cmp *);int (*fetch)(struct llist_head *,const void *,llist_cmp *,void *);void (*travel)(struct llist_head *,llist *op);
}LLSIT;LLIST *llist_create(int initsize);
void llist_destroy(LLIST *);#endif

llist.c

#include<stdio.h>
#include<stdlib.h>
#include"llist.h"
#include<string.h>int llist_insert(LLIST *,const void *data,int mode);
void *llist_find(LLIST *, const void *key, llist_cmp *);//数据类型不统一使用void 百搭
int llist_delete(LLIST *,const void *key,llist_cmp *);
int llist_fetch(LLIST *,const void *key,llist_cmp *,void *data);
void llist_travel(LLIST *,llist_op *);LLIST *llist_create(int initsize)//只包含一个头节点( 双向循环链表)
{LLIST *new;new = malloc(sizeof(*new));if(new == NULL)return NULL;new->size= initsize;new->head.prev = new->head;new->head.next = new->head;new->insert = llist_insert;// llist_insert 不加括号是指针赋值 new->delete = llist_delete;new->find = llist_find;new->fetch = llist_fetch;new->travel = llist_travel;return new;
}
int llist_insert(LLIST *ptr,const void *data,int mode)
{struct llist_node_st *newnode;newnode = malloc(sizeof(*newnode) + ptr->size);if(newnode == NULL)return -1;memcpy(newnode->data,data,ptr->size);if(mode == LLIST_FORWARD){newnode->prev = &ptr->head;newnode->next = ptr->head.next;newnode->prev->next = newnode;//头节点的 next 指针设置为指向新节点 newnodenewnode->next->prev = newnode;//原本在头节点之后的节点的 prev 指针设置为指向新节点 newnode。}else if(mode == LLIST_BACKWARD){newnode->prev = ptr->head.prev;newnode->next = &ptr->head;newnode->prev->next = newnode;newnode->next->prev = newnode;}else{return -3;}return 0;
}
static struct list_node_st * find_(LLIST *ptr, const void *key, llist_cmp *cmp)
{struct llist_node_st *cur;for(cur = ptr->head.next;cur!=ptr.head;cur=cur->next){if(cmp(key,cur->data) == 0)break;}return cur;
}void *llist_find(LLIST *ptr, const void *key, llist_cmp *cmp)
{	struct llist_node_st *node;node = find_(ptr,key,cmp);if(node = &ptr->head)return NULL;return node->data;
}
int llist_delete(LLIST *ptr,const void *key,llist_cmp *cmp)
{	struct llist_node_st *node;node = nodefind_(ptr,key,cmp);if(node == &ptr->head)return -1;node->prev->next = node->next;node->next->prev = node->prev;free(node);return 0;
}
int llist_fetch(LLIST *ptr,const void *key,llist_cmp *cmp,void *data)
{struct llist_node_st *node;node = nodefind_(ptr,key,cmp);if(node == &ptr->head)return -1;node->prev->next = node->next;node->next->prev = node->prev;if(data!=NULL)memcpy(data,node->data,ptr->size);free(node);return 0;
}
void llist_travel(LLIST *ptr,llist_op *op)//需要一个回调函数,需要用户给我传一个函数
{struct llist_node_st *cur;for(cur = ptr->head.next;cur!=&ptr->head;cur=cur->next)//为了封装成更通用的函数,不知道用户的结构类型,因此需要回调函数,且需要在 .h文件中使用 void 函数声明,且使用typedef重命名 看起来更好一些op(cur->data);//借用户之手,把他知道的数据类型打印了出来  具有通用性}
void llist_destroy(LLIST *ptr)
{struct llist node_st *cur,*next;for(cur= ptr->head.next;cur != &ptr->head;cur= next){next = cur->next;free(cur);}free(ptr);
}

main.c


#include<stdio.h>
#include<stdlib.h>
#include"llist.h"#define NAMESIZE	32struct score_st
{int id;char name[NAMESIZE];int math;int chinese;
};static void print_s(const void *record)
{const struct score_st *r = record;printf("%d %s %d %d\n",r->id,r->name,r->math,r->chinese);
}static int id_cmp(const void *key,const void *record)
{const int *k = key;const struct score_st *r = record;return (*k - r->id);}
static int name_cmp(const void *key,const void *record)
{const char *k = key;const struct score_st *r = record;return strcmp(k,r->name);
}int main()
{int ret,i;int id =3;LLIST *handler;struct score_st tmp;handler = llist_create(sizeof(struct score_st));if(handler == NULL)exit(1);for(i =0;i<7;i++){	tm.id =i;snprintf(tmp.name,NAMESIZE,"std%d",i);tmp.math = rand()%100;tmp.chinese = rand%()100;ret = handler->insert(handler,&tmp,LLIST_FORWARD);if(ret)exit(1);}handler->travel(handler,print_s);#if 0char *del_name = "std6";ret = llist_delete(handler,&id,id_cmp);//ret = llist_delete(handler,del_name,name_cmp)//如何实现根据任何字段来删除if(ret)printf("llist_delete failed!\n");llist_travel(handler,print_s);printf("\n\n");struct score *data;data = llist_find(handler,&id,id_cmp);if(data == NULL)printf("Can not find!\n");elseprintf_s(data);
#endifllist_destroy(handler);exit(0);
}

Makefile

all:llist
llist:llist.o main.o$(CC) $^ -o $@
clean:rm llist *.o -rf

补充说明

int (*insert)(LLIST *, const void *, int); 这是一个指向函数的指针。为了更好地理解,我们将其分解并详细解释:

解释

  1. 函数指针

    • int (*insert)(LLIST *, const void *, int); 声明了一个指向函数的指针 insert
    • insert 指向的函数返回 int 类型,并且接受三个参数。
  2. 参数

    • 第一个参数是一个指向 LLIST 类型的指针。
    • 第二个参数是一个指向 const void 类型的指针。
    • 第三个参数是一个 int 类型的变量。

更详细的分解

  • int (*insert)insert 是一个指向返回类型为 int 的函数的指针。
  • (LLIST *, const void *, int):这个函数接受三个参数:
    • LLIST *:指向 LLIST 类型的指针。
    • const void *:指向常量 void 类型的指针,表示这个指针指向的数据不会被修改。
    • int:一个整数类型。

示例

为了更好地理解,我们可以考虑一个示例,其中有一个函数与上述函数指针匹配:

假设有一个函数 insert_function,其定义如下:

int insert_function(LLIST *list, const void *data, int position) {// 插入操作的具体实现return 0; // 假设返回 0 表示成功
}

你可以将 insert 函数指针指向 insert_function

int (*insert)(LLIST *, const void *, int) = insert_function;

之后,你可以通过 insert 函数指针调用 insert_function

LLIST *myList;
const void *data;
int position = 5;
int result = insert(myList, data, position);

用途

函数指针非常有用,尤其在以下情况下:

  1. 回调函数:你可以将函数指针作为参数传递给另一个函数,以便在特定事件或条件发生时调用该函数。
  2. 动态函数调用:可以在运行时决定调用哪个函数,而不是在编译时确定。
  3. 实现多态性:在面向对象编程中,函数指针可以用于实现类似于C++中的虚函数的行为。

总结

int (*insert)(LLIST *, const void *, int); 是一个函数指针,指向一个接受 LLIST *const void *int 参数并返回 int 的函数。通过这种声明方式,可以灵活地在程序中使用函数指针,以实现动态函数调用和回调机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/839234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

期货学习笔记-横盘行情学习1

横盘行情的特征及分类 横盘行情的概念 横盘行情时中继形态的一种&#xff0c;一般常出现在大涨或大跌之后出现横盘行情是对当前趋势行情的修正&#xff0c;是对市场零散筹码的清理&#xff0c;是为了集中筹码更便于后期行情的展开 横盘行情的特征 1.水平运动&#xff1a;该…

【文献阅读】ESG评级不确定性对企业绿色创新的影响研究

ESG评级不确定性对企业绿色创新的影响研究 张张张三丰de思考与感悟 论文内容总结&#xff1a;本文主要是关于ESG评级不确定性&#xff08;也即来自三个评级机构的ESG得分差异&#xff09;&#xff0c;对企业绿色创新的影响。并且有效地区分了创新数量和创新质量。创新数量的是专…

iptablese防火墙【SNAT和DNAT】

目录 1.SNAT策略及应用 1.1SNAT原理与应用 1.2 SNAT策略的工作原理 1.3 实验操练 2.DNAT策略 2.1 DNAT策略的概述 2.2 DNAT原理与应用 2.3 实验操练 1.SNAT策略及应用 1.1SNAT原理与应用 SNAT 应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xf…

uniappx 应用未读角标插件(完善推送、通知、消息效果) Ba-Shortcut-Badge-U

简介&#xff08;下载地址&#xff09; Ba-Shortcut-Badge-U 是设置应用未读角标的原生插件&#xff0c;UTS版本&#xff08;同时支持uniapp和uniappx&#xff09; 支持设置未读、清空未读支持机型有Huawei、oppo、xiaomi、Samsung、Sony、LG、HTC、ZUK、ASUS、ADW、APEX、NO…

快速将短信从 Android 传输到 iPhone [支持 iPhone 15]

短信中包含有关工作和生活的重要信息&#xff0c;因此当我们拿到新手机时&#xff0c;它们不会丢失。今天&#xff0c;我们要讨论的是如何将短信从Android快速传输到iPhone&#xff0c;包括最新的iPhone 15。在Android和iOS操作系统之间&#xff0c;我们可以轻松地将短信从Andr…

C语言(指针)8

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

BUUCTF-misc23

[GUET-CTF2019]zips1 1.打开附件 是一个加密的压缩包 2.暴力破解压缩包 得到压缩包密码 3.010Editor 解密之后又得到一个加密的压缩包 发现不是解密文件 用010Editor打开是伪加密&#xff0c;将09 00 给为 00 00 4.Python 用Python运行文件里的部分内容&#xff0c;发现密…

高精度可编程直流电源功能

在电子产品开发和测试中&#xff0c;高精度可编程直流电源是一种非常重要的工具。它不仅能够提供稳定可靠的电源供应&#xff0c;还能够精确地控制输出电压和电流,满足各种复杂的测试需求。下面我们就来详细了解一下高精度可编程直流电源的功能特点。 1. 输出精度高 高精度可…

基于深度学习OCR文本识别

第一步&#xff1a;概要 基于深度学习OCR文本识别分为两个模块&#xff1a;DBNet和CRNN。 DBNet是基于分割的文本检测算法&#xff0c;算法将可微分二值化模块(Differentiable Binarization)引入了分割模型&#xff0c;使得模型能够通过自适应的阈值图进行二值化&#xff0c;并…

php发送短信功能(创蓝短信)

一、以下是创蓝发送短信的功能&#xff0c;可以直接执行&#xff1a; <?php$phone 12312312312;$msg 测试短信功能;echo 发送手机号&#xff1a;.$phone.<br/>;echo 发送内容&#xff1a;.$msg.<br/>;$send sendMessage($phone, $msg);var_dump($send);…

HQL面试题练习 —— 品牌营销活动天数

题目来源&#xff1a;小红书 目录 1 题目2 建表语句3 题解 1 题目 有营销活动记录表&#xff0c;记录了每个品牌每次营销活动的开始日期和营销活动的结束日期&#xff0c;现需要统计出每个品牌的总营销天数。 注意&#xff1a; 1:苹果第一行数据的营销结束日期比第二行数据的营…

sam代码简析

Segment Anything&#xff1a;建立了迄今为止最大的分割数据集&#xff0c;在1100万张图像上有超过1亿个掩码&#xff0c;模型的设计和训练是灵活的&#xff0c;其重要的特点是Zero-shot(零样本迁移性)转移到新的图像分布和任务&#xff0c;一个图像分割新的任务、模型和数据集…

记录centos中操作(查找、结束、批量)进程以及crontab定时写法的知识

环境&#xff1a;vps&#xff0c;centos7&#xff0c;python3。 近期写了个python程序&#xff0c;用青龙面板在centos上运行。程序中有while无限循环&#xff0c;但是我在青龙中设置了定时任务&#xff08;每隔半小时运行一次&#xff09;&#xff0c;于是造成了进程中有多个…

Java进阶16 单元测试XML注解

Java进阶16 一、单元测试 单元测试就是针对最小的功能单元编写测试代码&#xff0c;Java程序最小的功能单元是方法&#xff0c;因此&#xff0c;单原测试就是针对Java方法的测试&#xff0c;进而检查方法的正确性。简单理解就是测试代码的工具。 1、Junit 1.1 Junit引入 目…

全面了解CC攻击和防范策略

前言 “ CC攻击的原理就是攻击者控制某些主机不停地发大量数据包给对方服务器造成服务器资源耗尽&#xff0c;一直到宕机崩溃。” 什么是CC攻击&#xff1f; CC攻击前身是一个名为Fatboy的攻击程序&#xff0c;而之所以后来人们会称之为CC&#xff0c;也叫HTTP-FLOOD&#xff…

程序语言基础知识

文章目录 1.程序设计语言2. 程序设计语言的特点和分类3. 编译程序&#xff08;编译器&#xff09;的工作原理4. 程序语言的数据成分4.1 数据成分4.2 运算成分4.3 控制成分4.4 传输成分 1.程序设计语言 低级语言&#xff1a;机器语言和汇编语言。 机器语言&#xff1a;二进制代…

Java面向对象-常用类 (包装类)

常用类 – 包装类 基本数据类型的包装类 理解&#xff1a;包装类是8种基本数据类型对应的类 出现原因&#xff1a;Java是一种纯面向对象语言&#xff0c;但是java中有8种基本数据类型&#xff0c;破坏了java为纯面向对象的特征。为了承诺在java中一切皆对象&#xff0c;java…

c/c++ 判断质数(素数)

目录 一.常规方法 二.进阶方法 三.代码示例&#xff08;运用进阶方法&#xff09; 质数是整数且仅能被自身和1整除 一.常规方法 所以我们根据质数的这个定义便可用以下思路判断&#xff1a;设需要检测的数为x。y为除1和自己的除数 逐步检测x是否可被y整除&#xff0c;如x…

MySQL之架构设计与历史(一)

架构设计与历史 概述 和其他数据库系统相比&#xff0c;MySQL有点与众不同&#xff0c;它的架构可以在多种不同场景中应用并发挥好的作用&#xff0c;但同时也会带来一点选择上的困难。MySQL并不完美&#xff0c;却足够灵活&#xff0c;能够适应高要求的环境&#xff0c;例如…

Android 逆向学习【1】——版本/体系结构/代码学习

#Android 历史版本 参考链接&#xff1a;一篇文章让你了解Android各个版本的历程 - 知乎 (zhihu.com) 三个部分&#xff1a;api等级、版本号、代号&#xff08;这三个东西都是指的同一个系统&#xff09; API等级&#xff1a;在APP开发的时候写在清单列表里面的 版本号&…