C/C++ 进程间通信system V IPC对象超详细讲解(系统性学习day9)

news2024/11/27 12:38:40

目录

前言

一、system V IPC对象图解

1.流程图解:

​编辑

2.查看linux内核中的ipc对象:

 二、消息队列

1.消息队列的原理

2.消息队列相关的API

2.1 获取或创建消息队列(msgget)

 实例代码如下:

2.2 发送消息到消息队列中

 实例代码如下:

2.3 从消息队列中获取消息  

实例代码如下: 

 2.4 消息队列相关的命令

 2.5 管理消息队列

 实例代码如下:

三、共享内存

1.概念

2.原理图解

3.相关的api函数

3.1 创建共享内存对象

 实例代码如下:

3.2 映射共享内存  

 3.3 取消映射

 实例代码reader.c如下:

 实例代码writer.c如下:

实例代码shm.h 如下:

 3.4 管理共享内存

实例代码如下: 

四、信号灯 (信号量)

1.概念

2.信号灯相关的api函数

2.1 创建或获取信号灯对象

实例代码如下:

2.2 实现P操作和V操作  

实例代码如下:

2.3 管理信号灯  

实例代码如下: 

总结


前言

System V IPC(Inter-Process Communication)对象是一种用于在不同进程之间进行通信的机制。它包括三种类型的对象:消息队列(Message Queue)、信号量(Semaphore)和共享内存(Shared Memory)。


一、system V IPC对象图解

1.流程图解:

2.查看linux内核中的ipc对象:

 二、消息队列

1.消息队列的原理

 

2.消息队列相关的API

2.1 获取或创建消息队列(msgget)

头文件:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
//参数1  ----- key :
               动态获取key:  ftok()
                    #include <sys/types.h>
                    #include <sys/ipc.h>
                    key_t ftok(const char *pathname, int proj_id);
                    //参数1  ---- 工程目录
                    //参数2  ---- 工程编号
                    //返回值 ----- 成功,返回key值,失败:-1
               静态分配: IPC_PRIVATE
//参数2 ------ msgflg:如果消息队列不存在,需要给出创建的关键字,并设置权限 IPC_CREAT | 0666
//返回值 ----- 成功:消息队列的ID,失败:-1    

 实例代码如下:
int main(void)
{
    key_t key;
    int msg_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取消息对象
    msg_id = msgget(key,IPC_CREAT|0666);
    if(msg_id < 0){
        perror("msgget");
        exit(1);
    }
    return 0;
}

2.2 发送消息到消息队列中

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//参数1 ----- 消息队列的id
//参数2 -----  指向struct msgbuf结构体的指针,该结构体需要自己定义,如下:
                struct msgbuf {
                   long mtype;       /* message type, must be > 0 消息类型*/
                   char mtext[1];    /* message data  消息正文*/
                };
//参数3 ---- 要发送的消息的长度 
//参数4 ---- msgflg值如下:
            IPC_NOWAIT  消息没有发送完成函数也会立即返回。
            0:直到发送完成函数才返回
//返回值 --- 成功:0,失败:-1

 实例代码如下:
int main(void)
{
    key_t key;
    int msg_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取消息对象
    msg_id = msgget(key,IPC_CREAT|0666);
    if(msg_id < 0){
        perror("msgget");
        exit(1);
    }

//向消息队列中发送消息
    while(1){
        bzero(&buf,sizeof(buf));
        printf("请输入消息的类型:");
        scanf("%ld",&buf.mtype);
        printf("请输入消息:");
        while(getchar() != '\n');   //清空输入缓冲区
        fgets(buf.mtext,sizeof(buf.mtext),stdin);
        buf.mtext[strlen(buf.mtext)-1] = '\0';
        if(msgsnd(msg_id,&buf,strlen(buf.mtext),0) < 0){
            perror("msgsnd");
            exit(1);
        }
    }

    return 0;
}

2.3 从消息队列中获取消息  

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//参数1 ----- 消息队列的id
//参数2 -----  指向struct msgbuf结构体的指针,该结构体需要自己定义,如下:
                struct msgbuf {
                   long mtype;       /* message type, must be > 0 消息类型*/
                   char mtext[1];    /* message data  消息正文*/
                };
//参数3 ----  mtext的长度
//参数4 ----  msgtyp:要接收的消息类型
             msgtyp > 0 ,表示接收指定类型的消息 
             msgtyp = 0 ,按先后顺序依次接收不同类型消息
             msgtyp < 0 ,优先接收消息类型不大于|msgtyp|的最小类型的消息
//参数5 ---- msgflg值如下:
            IPC_NOWAIT  消息没有发送完成函数也会立即返回。
            0:直到发送完成函数才返回
//返回值 --- 成功:0,失败:-1

实例代码如下: 
int main(void)
{
    key_t key;
    int msg_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取消息对象
    msg_id = msgget(key,IPC_CREAT|0666);
    if(msg_id < 0){
        perror("msgget");
        exit(1);
    }

//从消息队列中获取消息
    while(1){
        bzero(&buf,sizeof(buf));
        printf("请输入消息的类型:");
        scanf("%ld",&buf.mtype);
        if(msgrcv(msg_id,&buf,sizeof(buf.mtext),buf.mtype,0) < 0){
            perror("msgsnd");
            exit(1);
        }
        printf("msg:%s\n",buf.mtext);
    }

    return 0;
}

 2.4 消息队列相关的命令

peter@ubuntu:~/2308/proc/day04_code$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages
0x0a010356 0          peter      666        61           5

peter@ubuntu:~/2308/proc/day04_code$ ipcrm -q 0
peter@ubuntu:~/2308/proc/day04_code$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

 2.5 管理消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 //参数1 ---- 消息队列ID
 //参数2 ---- 功能码:
                 IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
                IPC_SET:设置消息队列的属性。这个值取自buf参数。
                IPC_RMID:从系统中删除消息队列。    
 //参数3 ----struct msqid_ds 结构体指针
                 struct msqid_ds {
                       struct ipc_perm msg_perm;     /* Ownership and permissions */
                       time_t          msg_stime;    /* Time of last msgsnd(2) */
                       time_t          msg_rtime;    /* Time of last msgrcv(2) */
                       time_t          msg_ctime;    /* Time of last change */
                       unsigned long   __msg_cbytes; /* Current number of bytes in
                                                        queue (nonstandard) */
                       msgqnum_t       msg_qnum;     /* Current number of messages
                                                        in queue */
                       msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                        allowed in queue */
                       pid_t           msg_lspid;    /* PID of last msgsnd(2) */
                       pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
                   };

 实例代码如下:
int main(int argc,char **argv)
{
    int msg_id;

    msg_id = atoi(argv[1]);

    if(msgctl(msg_id,IPC_RMID,NULL) < 0){
        perror("msgget");
        exit(1);
    }
    return 0;
}

三、共享内存

1.概念

共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝

为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间

进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等 

2.原理图解

 

 

3.相关的api函数

3.1 创建共享内存对象

头文件:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
//参数1 ---- 动态获取key:  ftok()
                    #include <sys/types.h>
                    #include <sys/ipc.h>
                    key_t ftok(const char *pathname, int proj_id);
                    //参数1  ---- 工程目录
                    //参数2  ---- 工程编号
                    //返回值 ----- 成功,返回key值,失败:-1
               静态分配: IPC_PRIVATE
//参数2 ---- 要创建或获取的共享内存的大小
//参数3 ---- 权限:IPC_CREAT | 0666
//返回值 ---- 成功:共享内存ID,失败:-1

 实例代码如下:
int main(void)
{
    key_t key;
    int shm_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取共享内存对象
    shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);
    if(shm_id < 0){
        perror("shmget");
        exit(1);
    }
    return 0;
}

3.2 映射共享内存  

void *shmat(int shmid, const void *shmaddr, int shmflg);
//参数1 ---- 共享内存ID
//参数2 ---- 指定进程虚拟空间的映射的起始地址,一般为NULL:让系统分配一个起始地址
//参数3 ---- 访问权限:SHM_RDONLY:共享内存只读
                    默认0:共享内存可读写
//返回值 --- 成功:映射的虚拟空间地址,失败:-1

 3.3 取消映射

int shmdt(const void *shmaddr);
//参数 ----映射的虚拟空间的起始地址
//返回值 ----成功:0,失败:-1

 实例代码reader.c如下:
#include "shm.h"

int main(void)
{
    key_t key;
    int shm_id;
    char *buf;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
	perror("ftok");
	exit(1);
    }
    //创建或获取共享内存对象
    shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);
    if(shm_id < 0){
	perror("shmget");
	exit(1);
    }

    //将共享内存映射到进程的虚拟空间中
    buf = (char*)shmat(shm_id,NULL,0);
    if(buf < 0){
	perror("shmat");
	exit(1);
    }


    //打印共享内存中的数据
    while(1){
	printf("%s",buf);
	sleep(1);
    }

    //解除映射
    if(shmdt(buf) < 0){
	perror("shmdt");
	exit(1);
    }
    return 0;
}
 实例代码writer.c如下:
#include "shm.h"

int main(void)
{
    key_t key;
    int shm_id;
    char *buf;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
	perror("ftok");
	exit(1);
    }
    //创建或获取共享内存对象
    shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);
    if(shm_id < 0){
	perror("shmget");
	exit(1);
    }

    //将共享内存映射到进程的虚拟空间中
    buf = (char*)shmat(shm_id,NULL,0);
    if(buf < 0){
	perror("shmat");
	exit(1);
    }


    //向共享内存写数据
    while(1){
	printf("请输入字符串:");
	fgets(buf,SHM_SIZE,stdin);
    }

    //解除映射
    if(shmdt(buf) < 0){
	perror("shmdt");
	exit(1);
    }
    return 0;
}
实例代码shm.h 如下:
#ifndef __SHM_H__
#define __SHM_H__

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE  1024

#endif

 3.4 管理共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//参数1 ---- 共享内存的ID
//参数2 ---- 功能码:
            IPC_STAT  (获取对象属性)
            IPC_SET (设置对象属性)
            IPC_RMID (删除对象)    
//参数3 ----struct shmid_ds 结构体指针
            struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };

实例代码如下: 
int main(int argc,char **argv)
{
    int shm_id;

    shm_id = atoi(argv[1]);

    if(shmctl(shm_id,IPC_RMID,NULL) < 0){
        perror("shmget");
        exit(1);
    }
    return 0;
}

四、信号灯 (信号量)

1.概念

信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制。
信号灯种类:
    posix有名信号灯
    posix基于内存的信号灯(无名信号灯)
    System V信号灯(IPC对象)

1》 二值信号灯:用于表示资源是否可用
    值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0。

2》 计数信号灯:用于表示资源的数量
    值在0到n之间。用来统计资源,其值代表可用资源数

3》 等待操作,也称为P操作
    是等待信号灯的值变为大于0,然后将其减1;
4》 释放操作,也称为V操作
     用来唤醒等待资源的进程或者线程

5》System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯
 

2.信号灯相关的api函数

2.1 创建或获取信号灯对象

头文件:

 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
//参数1 ---- 动态获取key:  ftok()
                    #include <sys/types.h>
                    #include <sys/ipc.h>
                    key_t ftok(const char *pathname, int proj_id);
                    //参数1  ---- 工程目录
                    //参数2  ---- 工程编号
                    //返回值 ----- 成功,返回key值,失败:-1
               静态分配: IPC_PRIVATE
//参数2 ---- 集合中信号灯的个数
//参数3 ---- 访问权限:IPC_CREAT | 0666
//返回值 ---- 成功:信号灯对象ID,失败:-1

实例代码如下:
int main(void)
{
    key_t key;
    int sem_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取共享内存对象
    sem_id = semget(key,1, IPC_CREAT|0666);
    if(sem_id < 0){
        perror("semget");
        exit(1);
    }
    return 0;
}

2.2 实现P操作和V操作  

int semop(int semid, struct sembuf *sops, size_t nsops);
//参数1 ----- 信号灯对象的ID
//参数2 ----- 结构体指针
                struct sembuf {
                   short  sem_num;  //  要操作的信号灯的编号
                   short  sem_op;   //    0 :  等待,直到信号灯的值变成0
                                               //   1  :  释放资源,V操作
                                               //   -1 :  分配资源,P操作                   
                   short  sem_flg; // 0,  IPC_NOWAIT,  SEM_UNDO
                };

//参数3 -----nops:  要操作的信号灯的个数
//返回值 ---- 成功:0,失败:-1

实例代码如下:
//1》实现P操作
	void sem_p(int sem_id,int index)
    {
        struct sembuf buf = {index,-1,0};

        if(semop(sem_id,&buf,1) < 0){
            perror("semop");
            exit(1);
        }
    }
//2》实现v操作
    void sem_v(int sem_id,int index)
    {
        struct sembuf buf = {index,1,0};

        if(semop(sem_id,&buf,1) < 0){
            perror("semop");
            exit(1);
        }
    }

2.3 管理信号灯  

int semctl(int semid, int semnum, int cmd, ...);
//参数1 ---- 信号灯对象ID
//参数2 ---- 集合中信号灯的编号
//参数3 ---- 功能码:
            IPC_STAT  ----获取信号灯对象属性
            IPC_SET   ----设置信号灯对象属性
            IPC_RMID  ----从内核中删除信号灯对象
            SETALL      ----设置集合中所有信号灯的值
            SETVAL    ----设置集合中编号为semnum的信号灯的值
//参数4 ---- 联合体变量,类型如下:
            union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           };
//返回值 -----成功:0,失败:-1

实例代码如下: 
//初始化指定的信号灯
void sem_init(int sem_id,int semnum,int value)
{
    union semun su;
    su.val = value;
    if(semctl(sem_id,semnum,SETVAL,su) < 0){
        perror("semctl");
        exit(1);
    }
}
//初始化所有信号灯
void sem_init_all(int sem_id,unsigned short vals[])
{
    union semun su;
    su.array = vals;
    if(semctl(sem_id,0,SETALL,su) < 0){
        perror("semctl");
        exit(1);
    }
}


总结

        本篇文章针对进程间通信system V IPC对象进行详细讲解,希望能够帮到大家!

        以后还会给大家展现更多关于嵌入式和C语言的其他重要的基础知识,感谢大家支持懒大王!

       希望这篇博客能给各位朋友们带来帮助,最后懒大王请来过的朋友们留下你们宝贵的三连以及关注,感谢你们!

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

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

相关文章

关于Jupyter markdown的使用

一级标题 #空格 标题1 二级标题 ## 空格 标题2 三级标题 ###空格 标题3 无序&#xff1b; 有序&#xff1a; 数学符号&#xff1a;

MySQL:主从复制-基础复制(6)

环境 主服务器 192.168.254.1 从服务器&#xff08;1&#xff09;192.168.254.2 从服务器&#xff08;2&#xff09;192.168.253.3 我在主服务器上执行的操作会同步至从服务器 主服务器 yum -y install ntp 我们去配置ntp是需要让从服务器和我们主服务器时间同步 sed -i /…

1.Linux入门基本指令

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 01.ls指令 02.pwd指令 03.cd指令 04.touch指令 05.mkdir指令(重要) 06.rmdir&&rm指令(重要) 07.man指令(重要) 08.cp指令(重要) 09.mv指令(重要) 10.cat指令 nano指令 echo指令 输出重定向 追加重…

day25--JS进阶(递归函数,深浅拷贝,异常处理,改变this指向,防抖及节流)

目录 浅拷贝 1.拷贝对象①Object.assgin() ②展开运算符newObj {...obj}拷贝对象 2.拷贝数组 ①Array.prototype.concat() ② newArr [...arr] 深拷贝 1.通过递归实现深拷贝 2.lodash/cloneDeep实现 3.通过JSON.stringify()实现 异常处理 throw抛异常 try/catch捕获…

强化学习------DQN算法

简介 DQN&#xff0c;即深度Q网络&#xff08;Deep Q-network&#xff09;&#xff0c;是指基于深度学习的Q-Learing算法。Q-Learing算法维护一个Q-table&#xff0c;使用表格存储每个状态s下采取动作a获得的奖励&#xff0c;即状态-价值函数Q(s,a)&#xff0c;这种算法存在很…

数据结构:链式二叉树

上一章讲了堆,堆是完全二叉树的顺序存储结构,本章将要全面讲解一下二叉树的链式存储结构即链式二叉树 我们已经学习了二叉树的概念和性质了,本章重点学习二叉树相关操作,可以更好的理解分治算法思想;也需要对递归有更深次的理解. 其实普通的链式二叉树的增删查改没有什么意义,…

COLLABORATIVE DESIGNER FOR SOLIDWORKS® 新功能

共享和标注 优点&#xff1a;收件人在浏览器中访问共享文 件&#xff0c;无需安装3DEXPERIENCE 平台应用程序。 • 与 SOLIDWORKS 中来自您组织内部或外部的任何人无缝 共享您的设计。 • 直接将评论和标注附加到您的设计作品中&#xff0c;便于立即获得 反馈。 支持 SOLIDWO…

深入理解强化学习——强化学习的基础知识

分类目录&#xff1a;《深入理解强化学习》总目录 在机器学习领域&#xff0c;有一类任务和人的选择很相似&#xff0c;即序贯决策&#xff08;Sequential Decision Making&#xff09;任务。决策和预测任务不同&#xff0c;决策往往会带来“后果”&#xff0c;因此决策者需要为…

Centos7安装MongoDB7.xxNoSQL数据库|设置开机启动(骨灰级+保姆级)

一: mongodb下载 MongoDB 社区免费下载版 MongoDB社区下载版 [rootwww tools]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.1.0-rc4.tgz 二: 解压到指定目录 [rootwww tools]# mkdir -p /usr/local/mongodb [rootwww tools]# tar -zxvf mongodb-…

Linux目录和文件查看命令

一、Linux 的目录结构 Linux 的目录结构是一个树状结构&#xff0c;以根目录&#xff08;/&#xff09;为起点&#xff0c;以下是常见的 Linux 目录结构的主要内容&#xff1a; / 根路径 ├── bin: 存放系统指令&#xff08;命令&#xff09;&#xff0c;如ls、cp、mv等&…

ARM-流水灯

.text .global _start _start: 1、设置GPIOE寄存器的时钟使能 RCC_MP_AHB$ENSETR[4]->1 0x50000a28LDR R0,0X50000A28 LDR R1,[R0] 从R0起始地址的4字节数据取出放在R1 ORR R1,R1,#(0X3<<4) 第4位设置为1 STR R1,[R0] 写回2、设置PE10、PE8、PF10管脚为输出模式 …

Observability:使用 OpenTelemetry 对 Node.js 应用程序进行自动检测

作者&#xff1a;Bahubali Shetti DevOps 和 SRE 团队正在改变软件开发的流程。 DevOps 工程师专注于高效的软件应用程序和服务交付&#xff0c;而 SRE 团队是确保可靠性、可扩展性和性能的关键。 这些团队必须依赖全栈可观察性解决方案&#xff0c;使他们能够管理和监控系统&a…

学习记忆——数学篇——案例——算术——记忆100内质数

文章目录 质数表歌诀记忆法100以内的质数歌谣质数口决一百以内质数口诀100以内素数歌 规律记忆法100以内6的倍数前、后位置上的两个数&#xff0c;只要不是5或7的倍数&#xff0c;就一定是质数个数没有用该数除以包括7在内的质数 分类记忆法数字编码法谐音记忆法 100以内的质数…

Matlab随机变量的数字特征

目录 1、均值&#xff08;数学期望&#xff09; 2、中位数 3、几何平均数 4、调和平均数 5、数据排序 6、众数 7、极差&#xff08;最大值和最小值之差&#xff09; 8、方差与均方差&#xff08;标准差&#xff09; 9、变异系数 10、常见分布的期望与方差的计算 11、协方…

ElasticSearch 学习8 :ik分词器的扩展,及java调用ik分词器的analyzer

1.前言&#xff1a; 上篇已经说过ik的集成&#xff0c;这篇说下ik的实际使用 2.2、IK分词器测试 IK提供了两个分词算法ik_smart 和 ik_max_word ik_smart&#xff1a;为最少切分ik_max_word&#xff1a;为最细粒度划分。 2.2.1、最小切分示例 #分词器测试ik_smart POST _…

互联网项目有哪些值得做的

互联网已经融入了我们生活的方方面面&#xff0c;从电商巨头到科技创新&#xff0c;互联网带来的变革和便利无处不在。而在这个信息广泛的时代&#xff0c;越来越多的人开始思考如何利用互联网去创造价值。现如今&#xff0c;互联网项目的形式多种多样&#xff0c;有些让我们的…

剑指offer——JZ79 判断是不是平衡二叉树 解题思路与具体代码【C++】

一、题目描述与要求 判断是不是平衡二叉树_牛客题霸_牛客网 (nowcoder.com) 题目描述 输入一棵节点数为 n 二叉树&#xff0c;判断该二叉树是否是平衡二叉树。 在这里&#xff0c;我们只需要考虑其平衡性&#xff0c;不需要考虑其是不是排序二叉树 平衡二叉树&#xff08;…

【Java 进阶篇】深入了解HTML表单标签

HTML&#xff08;Hypertext Markup Language&#xff09;表单标签是网页开发中的重要组成部分&#xff0c;用于创建各种交互式元素&#xff0c;允许用户输入、提交和处理数据。本文将深入探讨HTML表单标签&#xff0c;包括如何创建表单、各种输入元素、表单属性以及一些最佳实践…

C++学习day2

作业&#xff1a; 1> 思维导图 2>自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和…

jenkins工具系列 —— 插件 使用Changelog获取commit记录

文章目录 安装changelog插件重启jenkins配置 ChangelogExecute shell 使用 changelog邮件中html格式也可以使用构建测试&#xff08;查看构建项 -> 控制台输出&#xff09; 安装changelog插件 插件文件可通过 V 获取 点击 左侧的 Manage Jenkins —> Plugins ——> …