Linux V4L2 应用编程

V4L2:Video4Linux2,是 Linux 内核中的一个框架,提供了一套用于视频设备驱动程序开发的 API。它是一个开放的、通用的、模块化的视频设备驱动程序框架,允许 Linux 操作系统和应用程序与各种视频设备(如摄像头、视频采集卡等)进行交互。

V4L2 提供了一个通用的 API,使应用程序能够访问和控制视频设备,包括获取设备信息、设置设备参数、采集视频数据、控制设备状态等。V4L2 还提供了一个统一的视频数据格式,允许应用程序在处理视频数据时无需考虑设备的具体格式。

Video for Linux two(Video4Linux2)简称 V4L2,是 V4L 的改进版。V4L2 支持三种方式来采集图像:内存映射方式(mmap)、直接读取方式(read)和用户指针。内存映射的方式采集速度较快,一般用于连续视频数据的采集,实际工作中的应用概率更高;直接读取的方式相对速度慢一些,所以常用于静态图片数据的采集;用户指针使用较少。

V4L2 的主要特性

  1. 模块化的架构:V4L2 是一个模块化的架构,允许多个设备驱动程序同时存在并共享同一个 API。每个设备驱动程序都是一个独立的内核模块,可以在运行时加载和卸载。这种架构可以使开发人员更容易地开发新的视频设备驱动程序,并允许多个驱动程序同时使用相同的 API。

  2. 统一的设备节点:V4L2 提供了一种统一的设备节点,使应用程序可以使用相同的方式访问不同类型的视频设备。这种节点通常是 /dev/videoX,其中 X 是一个数字,表示设备的编号。应用程序可以通过打开这个节点来访问设备,并使用 V4L2 API 进行数据采集和控制。

  3. 统一的视频数据格式:V4L2 提供了一个统一的视频数据格式,称为 V4L2_PIX_FMT,允许应用程序在处理视频数据时无需考虑设备的具体格式。V4L2_PIX_FMT 包括了许多常见的视频格式,如 RGB、YUV 等。应用程序可以使用 V4L2 API 来查询设备支持的数据格式,并选择适当的格式进行数据采集和处理。

  4. 支持多种视频设备:V4L2 支持许多不同类型的视频设备,包括摄像头、视频采集卡、TV 卡等。每个设备都有自己的驱动程序,提供了相应的 V4L2 API。这些驱动程序可以根据设备的不同特性,提供不同的采集模式、数据格式、控制参数等。

  5. 支持流式 I/O:V4L2 支持流式 I/O,即通过内存映射的方式将视频数据从设备直接传输到应用程序中。这种方式可以减少数据复制的次数,提高数据传输的效率。

  6. 支持控制参数:V4L2 允许应用程序通过 API 来控制视频设备的参数,包括亮度、对比度、色彩饱和度、曝光时间等。应用程序可以使用 V4L2 API 来查询设备支持的参数,并设置适当的值。

  7. 支持事件通知:V4L2 支持事件通知,当视频设备状态发生变化时,如视频信号丢失、帧率变化等,V4L2 驱动程序可以向应用程序发送通知,以便应用程序做出相应的处理。

从上面的特征可以看出,V4L2 提供了一套通用、灵活、可扩展的视频设备驱动程序框架,使得 Linux 操作系统和应用程序可以方便地与各种视频设备进行交互,并且不需要关心设备的具体实现细节。从而让开发人员能够更加专注于应用程序的开发。

V4L2 视频采集步骤


摄像头使用流程

  • open:打开设备节点 /dev/videoX
  • ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力,比如
    • 确认它是否是"捕获设备",因为有些节点是输出设备
    • 确认它是否支持 mmap 操作,还是仅支持 read/write 操作
  • ioctl VIDIOC_ENUM_FMT:枚举它支持的格式
  • ioctl VIDIOC_S_FMT:在上面枚举出来的格式里,选择一个来设置格式
  • ioctl VIDIOC_REQBUFS:申请 buffer,APP 可以申请很多个 buffer,但是驱动程序不一定能申请到
  • ioctl VIDIOC_QUERYBUF 和 mmap:查询 buffer 信息、映射
    • 如果申请到了 N 个 buffer,这个 ioctl 就应该执行 N 次
    • 执行 mmap 后,APP 就可以直接读写这些 buffer
  • ioctl VIDIOC_QBUF:把 buffer 放入"空闲链表"
    • 如果申请到了 N 个 buffer,这个 ioctl 就应该执行 N 次
  • ioctl VIDIOC_STREAMON:启动摄像头
  • 这里是一个循环:使用 poll/select 监测 buffer,然后从"完成链表"中取出 buffer,处理后再放入"空闲链表"
    • poll/select
    • ioctl VIDIOC_DQBUF:从"完成链表"中取出 buffer
    • 处理:前面使用 mmap 映射了每个 buffer 的地址,处理时就可以直接使用地址来访问 buffer
    • ioclt VIDIOC_QBUF:把 buffer 放入"空闲链表"
  • ioctl VIDIOC_STREAMOFF:停止摄像头

demo

#include <config.h>
#include <video_manager.h>
#include <disp_manager.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>static int g_aiSupportedFormats[] = {V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_RGB565};static int V4l2GetFrameForReadWrite(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf);
static int V4l2PutFrameForReadWrite(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf);
static T_VideoOpr g_tV4l2VideoOpr;static int isSupportThisFormat(int iPixelFormat)
{int i;for (i = 0; i < sizeof(g_aiSupportedFormats)/sizeof(g_aiSupportedFormats[0]); i++){if (g_aiSupportedFormats[i] == iPixelFormat)return 1;}return 0;
}/* 参考 luvcview *//* open* VIDIOC_QUERYCAP 确定它是否视频捕捉设备,支持哪种接口(streaming/read,write)* VIDIOC_ENUM_FMT 查询支持哪种格式* VIDIOC_S_FMT    设置摄像头使用哪种格式* VIDIOC_REQBUFS  申请buffer对于 streaming:* VIDIOC_QUERYBUF 确定每一个buffer的信息 并且 mmap* VIDIOC_QBUF     放入队列* VIDIOC_STREAMON 启动设备* poll            等待有数据* VIDIOC_DQBUF    从队列中取出* 处理....* VIDIOC_QBUF     放入队列* ....对于read,write:read处理....read* VIDIOC_STREAMOFF 停止设备**/static int V4l2InitDevice(char *strDevName, PT_VideoDevice ptVideoDevice)
{int i;int iFd;int iError;struct v4l2_capability tV4l2Cap;struct v4l2_fmtdesc tFmtDesc;struct v4l2_format  tV4l2Fmt;struct v4l2_requestbuffers tV4l2ReqBuffs;struct v4l2_buffer tV4l2Buf;int iLcdWidth;int iLcdHeigt;int iLcdBpp;iFd = open(strDevName, O_RDWR);if (iFd < 0){DBG_PRINTF("can not open %s\n", strDevName);return -1;}ptVideoDevice->iFd = iFd;iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);memset(&tV4l2Cap, 0, sizeof(struct v4l2_capability));iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);if (iError) {DBG_PRINTF("Error opening device %s: unable to query device.\n", strDevName);goto err_exit;}if (!(tV4l2Cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){DBG_PRINTF("%s is not a video capture device\n", strDevName);goto err_exit;}if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING) {DBG_PRINTF("%s supports streaming i/o\n", strDevName);}if (tV4l2Cap.capabilities & V4L2_CAP_READWRITE) {DBG_PRINTF("%s supports read i/o\n", strDevName);}memset(&tFmtDesc, 0, sizeof(tFmtDesc));tFmtDesc.index = 0;tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {if (isSupportThisFormat(tFmtDesc.pixelformat)){ptVideoDevice->iPixelFormat = tFmtDesc.pixelformat;break;}tFmtDesc.index++;}if (!ptVideoDevice->iPixelFormat){DBG_PRINTF("can not support the format of this device\n");goto err_exit;        }/* set format in */GetDispResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4l2Fmt.fmt.pix.pixelformat = ptVideoDevice->iPixelFormat;tV4l2Fmt.fmt.pix.width       = iLcdWidth;tV4l2Fmt.fmt.pix.height      = iLcdHeigt;tV4l2Fmt.fmt.pix.field       = V4L2_FIELD_ANY;/* 如果驱动程序发现无法某些参数(比如分辨率),* 它会调整这些参数, 并且返回给应用程序*/iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt); if (iError) {DBG_PRINTF("Unable to set format\n");goto err_exit;        }ptVideoDevice->iWidth  = tV4l2Fmt.fmt.pix.width;ptVideoDevice->iHeight = tV4l2Fmt.fmt.pix.height;/* request buffers */memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));tV4l2ReqBuffs.count = NB_BUFFER;tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);if (iError) {DBG_PRINTF("Unable to allocate buffers.\n");goto err_exit;        }ptVideoDevice->iVideoBufCnt = tV4l2ReqBuffs.count;if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING){/* map the buffers */for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++) {memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));tV4l2Buf.index = i;tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4l2Buf.memory = V4L2_MEMORY_MMAP;iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);if (iError) {DBG_PRINTF("Unable to query buffer.\n");goto err_exit;}ptVideoDevice->iVideoBufMaxLen = tV4l2Buf.length;ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,tV4l2Buf.m.offset);if (ptVideoDevice->pucVideBuf[i] == MAP_FAILED) {DBG_PRINTF("Unable to map buffer\n");goto err_exit;}}        /* Queue the buffers. */for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++) {memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));tV4l2Buf.index = i;tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4l2Buf.memory = V4L2_MEMORY_MMAP;iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);if (iError){DBG_PRINTF("Unable to queue buffer.\n");goto err_exit;}}}else if (tV4l2Cap.capabilities & V4L2_CAP_READWRITE){g_tV4l2VideoOpr.GetFrame = V4l2GetFrameForReadWrite;g_tV4l2VideoOpr.PutFrame = V4l2PutFrameForReadWrite;/* read(fd, buf, size) */ptVideoDevice->iVideoBufCnt  = 1;/* 在这个程序所能支持的格式里, 一个象素最多只需要4字节 */ptVideoDevice->iVideoBufMaxLen = ptVideoDevice->iWidth * ptVideoDevice->iHeight * 4;ptVideoDevice->pucVideBuf[0] = malloc(ptVideoDevice->iVideoBufMaxLen);}ptVideoDevice->ptOPr = &g_tV4l2VideoOpr;return 0;err_exit:    close(iFd);return -1;    
}static int V4l2ExitDevice(PT_VideoDevice ptVideoDevice)
{int i;for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++){if (ptVideoDevice->pucVideBuf[i]){munmap(ptVideoDevice->pucVideBuf[i], ptVideoDevice->iVideoBufMaxLen);ptVideoDevice->pucVideBuf[i] = NULL;}}close(ptVideoDevice->iFd);return 0;
}static int V4l2GetFrameForStreaming(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
{struct pollfd tFds[1];int iRet;struct v4l2_buffer tV4l2Buf;/* poll */tFds[0].fd     = ptVideoDevice->iFd;tFds[0].events = POLLIN;iRet = poll(tFds, 1, -1);if (iRet <= 0){DBG_PRINTF("poll error!\n");return -1;}/* VIDIOC_DQBUF */memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4l2Buf.memory = V4L2_MEMORY_MMAP;iRet = ioctl(ptVideoDevice->iFd, VIDIOC_DQBUF, &tV4l2Buf);if (iRet < 0) {DBG_PRINTF("Unable to dequeue buffer.\n");return -1;}ptVideoDevice->iVideoBufCurIndex = tV4l2Buf.index;ptVideoBuf->iPixelFormat        = ptVideoDevice->iPixelFormat;ptVideoBuf->tPixelDatas.iWidth  = ptVideoDevice->iWidth;ptVideoBuf->tPixelDatas.iHeight = ptVideoDevice->iHeight;ptVideoBuf->tPixelDatas.iBpp    = (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_YUYV) ? 16 : \(ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_MJPEG) ? 0 :  \(ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_RGB565) ? 16 :  \0;ptVideoBuf->tPixelDatas.iLineBytes    = ptVideoDevice->iWidth * ptVideoBuf->tPixelDatas.iBpp / 8;ptVideoBuf->tPixelDatas.iTotalBytes   = tV4l2Buf.bytesused;ptVideoBuf->tPixelDatas.aucPixelDatas = ptVideoDevice->pucVideBuf[tV4l2Buf.index];    return 0;
}static int V4l2PutFrameForStreaming(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
{/* VIDIOC_QBUF */struct v4l2_buffer tV4l2Buf;int iError;memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));tV4l2Buf.index  = ptVideoDevice->iVideoBufCurIndex;tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4l2Buf.memory = V4L2_MEMORY_MMAP;iError = ioctl(ptVideoDevice->iFd, VIDIOC_QBUF, &tV4l2Buf);if (iError) {DBG_PRINTF("Unable to queue buffer.\n");return -1;}return 0;
}static int V4l2GetFrameForReadWrite(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
{int iRet;iRet = read(ptVideoDevice->iFd, ptVideoDevice->pucVideBuf[0], ptVideoDevice->iVideoBufMaxLen);if (iRet <= 0){return -1;}ptVideoBuf->iPixelFormat        = ptVideoDevice->iPixelFormat;ptVideoBuf->tPixelDatas.iWidth  = ptVideoDevice->iWidth;ptVideoBuf->tPixelDatas.iHeight = ptVideoDevice->iHeight;ptVideoBuf->tPixelDatas.iBpp    = (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_YUYV) ? 16 : \(ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_MJPEG) ? 0 :  \(ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_RGB565)? 16 : \0;ptVideoBuf->tPixelDatas.iLineBytes    = ptVideoDevice->iWidth * ptVideoBuf->tPixelDatas.iBpp / 8;ptVideoBuf->tPixelDatas.iTotalBytes   = iRet;ptVideoBuf->tPixelDatas.aucPixelDatas = ptVideoDevice->pucVideBuf[0];    return 0;
}static int V4l2PutFrameForReadWrite(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
{return 0;
}static int V4l2StartDevice(PT_VideoDevice ptVideoDevice)
{int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;int iError;iError = ioctl(ptVideoDevice->iFd, VIDIOC_STREAMON, &iType);if (iError) {DBG_PRINTF("Unable to start capture.\n");return -1;}return 0;
}static int V4l2StopDevice(PT_VideoDevice ptVideoDevice)
{int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;int iError;iError = ioctl(ptVideoDevice->iFd, VIDIOC_STREAMOFF, &iType);if (iError) {DBG_PRINTF("Unable to stop capture.\n");return -1;}return 0;
}static int V4l2GetFormat(PT_VideoDevice ptVideoDevice)
{return ptVideoDevice->iPixelFormat;
}/* 构造一个VideoOpr结构体 */
static T_VideoOpr g_tV4l2VideoOpr = {.name        = "v4l2",.InitDevice  = V4l2InitDevice,.ExitDevice  = V4l2ExitDevice,.GetFormat   = V4l2GetFormat,.GetFrame    = V4l2GetFrameForStreaming,.PutFrame    = V4l2PutFrameForStreaming,.StartDevice = V4l2StartDevice,.StopDevice  = V4l2StopDevice,
};/* 注册这个结构体 */
int V4l2Init(void)
{return RegisterVideoOpr(&g_tV4l2VideoOpr);
}

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

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

相关文章

外推蜘蛛池的优势及优势?

源码介绍&#xff1a; 适用使用范围: 外推蜘蛛池、站群 演示地址&#xff1a;以截图为准 运行环境&#xff1a;php 其余注释&#xff1a;什么是蜘蛛池&#xff1f; 蜘蛛池是一个利用大平台权重来获取百度收录和排名的程序。 程序员通常称其为“蜘蛛池”。 这是一个可以快速…

Spring-声明式事务实例(有详细注释)

前提知识 Spring-IOC容器注解方式使用https://blog.csdn.net/m0_61160520/article/details/136784799?spm1001.2014.3001.5501切点表达式https://blog.csdn.net/m0_61160520/article/details/136782885?spm1001.2014.3001.5501 案例 1.创建项目 2.导入依赖 <dependen…

Cache缓存:HTTP缓存策略解析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

select , poll, epoll思维导图

目录 1. 总的框架结构 2. select 3. poll 4. epoll 1. 总的框架结构 2. select

SpringCloud-Gateway服务网关

一、网关介绍 1. 为什么需要网关 Gateway网关是我们服务的守门神&#xff0c;所有微服务的统一入口。 网关的核心功能特性&#xff1a; 请求路由 权限控制 限流 架构图&#xff1a; 权限控制&#xff1a;网关作为微服务入口&#xff0c;需要校验用户是是否有请求资格&am…

使用阿里云服务器搭建网站教程,超简单10分钟网站上线

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网aliyunfuwuqi.com以搭建WordPress网站博客为例&#xff0c;来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流…

基于python+vue分类信息服务平台移动端的设计与实现flask-django-php-nodejs

分类信息服务平台是在Android操作系统下的应用平台。为防止出现兼容性及稳定性问题&#xff0c;框架选择的是django&#xff0c;Android与后台服务端之间的数据存储主要通过MySQL。用户在使用应用时产生的数据通过 python等语言传递给数据库。通过此方式促进分类信息服务平台信…

蓝桥杯单片机快速开发笔记——PCF8591的DAC模拟电压输出

一、原理分析 PCF8591电压信号探测器&#xff1a;http://t.csdnimg.cn/R38tC IIC原理&#xff1a;http://t.csdnimg.cn/v4dSv IIC指令&#xff1a;http://t.csdnimg.cn/RY6yi HC573/HC138&#xff1a;http://t.csdnimg.cn/W0a0U 数码管&#xff1a;http://t.csdnimg.cn/kfm9Y 独…

鸿蒙OpenHarmony开发实战:【MiniCanvas】

介绍 基于OpenHarmony的Cavas组件封装了一版极简操作的MiniCanvas&#xff0c;屏蔽了原有Canvas内部复杂的调用流程&#xff0c;支持一个API就可以实现相应的绘制能力&#xff0c;该库还在继续完善中&#xff0c;也欢迎PR。 使用说明 添加MiniCanvas依赖 在项目entry目录执行…

Docker学习笔记 - 常用命令

目录 基本概念常用命令使用docker compose启动脚本创建自己的image Docker命令文档 1. 下载一个image 从hub.docker.com下载一个image。 docker pull [image name]下载时指定image的tag。 docker pull [image name]:<tag>举例&#xff0c;下载postgre的tag为alpine…

CPP容器vector和list,priority_queue定义比较器

#include <iostream> #include <bits/stdc.h> using namespace std; struct VecCmp{bool operator()(int& a,int& b){return a>b;/*** 对于vector和list容器&#xff0c;这里写了&#xff1e;就是从大到小* 对于priority_queue容器&#xff0c;这里写…

配置java8和java11以及输入version命令没反应问题

电脑重置重新安装java8和java11记录一下供自己观看 安装过程掠过我自己能看懂就行 需要记录一下 因为JDK11以后Oracle把JRE集成到了JDK中&#xff0c;在安装JDK11及更高版本的JDK时&#xff0c;默认是不会自动安装JRE的。在jdk11的安装目录下打开命令行窗口或者shift鼠标右键…

婚恋小程序开发的资质需要哪些?维护成本高吗?

随着互联网的高速发展&#xff0c;人们的生活方式也在发生着翻天覆地的变化。在这个变化中&#xff0c;婚恋市场也逐渐走向了线上&#xff0c;婚恋小程序应运而生。那么&#xff0c;开发一款婚恋小程序需要哪些资质呢&#xff1f;维护成本又高不高呢&#xff1f;本文将从这两个…

2024年新算法!PCA+CPO+K-means聚类,冠豪猪优化器(CPO)优化K-means,适合学习,也适合发paper

2024年新算法&#xff01;PCACPOK-means聚类&#xff0c;冠豪猪优化器&#xff08;CPO)优化K-means&#xff0c;适合学习&#xff0c;也适合发paper。 一、冠豪猪优化器 摘要&#xff1a;受冠豪猪(crest Porcupine, CP)的各种防御行为启发&#xff0c;提出了一种新的基于自然…

【C语言】结构体与位段

一、前言 我们之前学习过定义一个整型类型的变量又或者定义一个浮点型类型的变量...&#xff0c;这些变量可以描述一个整数又或者描述一个小数...&#xff0c;可无论是整数还是小数...&#xff0c;它们也只是简单对象。如果我们想要描述像一本书&#xff0c;一个人这种复杂对象…

【算法与数据结构】深入二叉树实现超详解

文章目录 &#x1f4dd;前言&#x1f320; 接口函数✏️ 实现函数&#x1f309;创建树的新节点&#x1f320;通过前序遍历的数组构建二叉树&#x1f309;包装通过前序遍历的数组构建二叉树&#x1f320;二叉树的销毁&#x1f320;层次遍历&#x1f320;第一种实现&#xff1a;不…

Android: Gradle 命令

一、查看整个项目依赖传递关系 x.x.x (*) 该依赖已经有了&#xff0c;将不再重复依赖。x.x.x -> x.x.x 该依赖的版本被箭头所指的版本代替。x.x.x -> x.x.x(*) 该依赖的版本被箭头所指的版本代替&#xff0c;并且该依赖已经有了&#xff0c;不再重复依赖。 1. gradlew ap…

002 高并发内存池_定长内存池设计

​&#x1f308;个人主页&#xff1a;Fan_558 &#x1f525; 系列专栏&#xff1a;高并发内存池 &#x1f339;关注我&#x1f4aa;&#x1f3fb;带你学更多知识 文章目录 前言一、设计整体框架二、New操作&#xff08;申请空间&#xff09;三、Delete操作&#xff08;用自由链…

奇舞周刊第523期:来自 rust 生态的强烈冲击?谈谈 Leptos 在语法设计上的精妙之处...

奇舞推荐 ■ ■ ■ 来自 rust 生态的强烈冲击&#xff1f;谈谈 Leptos 在语法设计上的精妙之处 过去很长一段时间&#xff0c;前端框架们都在往响应式的方向发展。同时又由于 React hooks 的深远影响&#xff0c;函数式 响应式成为了不少前端心中最理想的前端框架模样。Solid …

设计模式-初步认识

目录 &#x1f6fb;1.什么是设计模式 &#x1f69a;2.设计模式的优点 &#x1f68d;3.设计模式6大原则 &#x1f6f4;4.设计模式类型 1.什么是设计模式 设计模式代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开…