【Linux】进程等待
文章目录
- 进程等待
- 进程等待必要性
- 实验(见见猪跑)
- 进程等待的方法
- wait方法
- waitpid**方法**
- 宏的使用方法
- 获取子进程status
- 阻塞VS非阻塞
- 概念对比
- 非阻塞有什么好处
- 具体代码实现
- 进程的阻塞等待方式:
- 进程的非阻塞等待方式:
- 让父进程做其他任务
进程等待
进程等待必要性
- 之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
- 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
- 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
因此,进程等待是为了:
- 避免内存泄漏
- 获取子进程执行的结果
- 代码跑完,结果对->退出码
- 代码跑完,结果不对->退出码
- 代码运行异常->信号
等待就是通过系统调用,获取子进程退出码或者退出信号的方式,顺便释放内存
实验(见见猪跑)
#include<sys/types.h>
#include<sys/wait.h>
int main()
{pid_t id = fork();if(id == 0){int cnt = 10;while(cnt){printf("我是子进程:%d,父进程:%d,cnt:%d\n",getpid(),getppid(),cnt--);}exit(10);//此时都是S状态}//此时子进程变成Z状态sleep(15);pid_t ret = wait(NULL);//回收子进程if(id > 0){printf("wait success:%d\n",ret);}sleep(5);//父进程退出
}
waitpid用法;
#include<sys/types.h>
#include<sys/wait.h>
int main()
{pid_t id = fork();if(id == 0)//子进程返回0,父进程返回子进程id{int cnt = 10;while(cnt){printf("我是子进程:%d,父进程:%d,cnt:%d\n",getpid(),getppid(),cnt--);}exit(10);//退出的三种结果://1.代码跑完,结果是对的//2.代码跑完,结果是错的//3.代码没有跑完,出异常了//此时都是S状态}//此时子进程变成Z状态sleep(15);int status = 0;pid_t ret = waitpid(id,&status,0);//回收子进程//错误观念:此时status的值变成了10//因为status不是被整体使用的,有自己的位图结构if(id > 0){printf("wait success:%d,sig number:%d,child exit code:%d\n",ret,(status & 0x7F),(status>>8)&0xFF);}//sig number:0 child exit code:10sleep(5);//父进程退出
}
监控脚本:
ps ajx | head -1 && ps axj | grep mytest | grep -v grep
第一句话是把标题拿出来
第二句话是把matest的进程信息拿出来
第三句话是把grep本身的进程信息去掉
之后写一个循环语句
while :; do ps ajx | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; done
(ctrl + z 退出)
进程等待的方法
wait方法
:!man 2 wait //可以查看到相关wait的用法信息
需要包含两个头文件:
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
waitpid方法
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数: pid: Pid=-1,等待任一个子进程。与wait等效。 Pid>0.等待其进程ID与pid相等的子进程。 status: WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码) options: WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
这里有一个问题:
status按理来说是会返回信号+退出码
的,但是一个整数怎么能返回两个整数呢?
所以我们不应该吧status当作一个完整的整数,而是应该看作位图结构(后面会详解)
宏的使用方法
int ret = waitpid(id,&status,0)
if(ret > 0)
{//是否正常退出if(WIFEXITED(status)){//判断子进程运行结果是否OKprintf("exit code :%d\n",WEXITSTATUS(status));}else//出异常了(比如kill -9){//TODOprintf("child exit not normal\n");}
}
- 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
- 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
- 如果不存在该子进程,则立即出错返回。
获取子进程status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(本身是32位,但是这里我们只研究status低16比特位):
1.代码跑完,结果是对的
2.代码跑完,结果是错的
3.代码没有跑完,出异常了
终止信号(即退出码)用来表示是否正常结束(为0则表示正常退出)
用退出状态表示结果是否正确(通过kill -l进行对应数字的查看(比如发生段错误,除0操作等)),此时退出码是否为0或者是其他的,都无意义,我们不讨论它.
第七位是信号编号
获取退出状态:
(status>>8)&0xFF )
获取终止信号:
``(status & 0x7F))
#include<sys/types.h>
#include<stdio.h>
#include<sys/wait.h>
int main()
{pid_t id = fork();if (id == 0){int cnt = 10;while (cnt){printf("我是子进程:%d,父进程:%d,cnt:%d\n", getpid(), getppid(), cnt--);sleep(1);}}int status = 0;//1.让OS释放子进程的僵尸状态//2.获取子进程的退出结果//3.在等待期间,子进程没有退出的时候,父进程只能阻塞等待.pid_t ret = waitpid(id, &status, 0);if (id > 0){printf("wait success: %d,sig number : %d ,child exit code: %d\n",ret,(status & 0x7F),(status>>8)&0xFF );}}
测试代码:
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main( void )
{ pid_t pid; if ( (pid=fork()) == -1 ) perror("fork"),exit(1); if ( pid == 0 ){ sleep(20); exit(10); } else { int st; int ret = wait(&st); if ( ret > 0 && ( st & 0X7F ) == 0 ){ // 正常退出 printf("child exit code:%d\n", (st>>8)&0XFF); } else if( ret > 0 ) { // 异常退出 printf("sig code : %d\n", st&0X7F ); } }
}
测试结果: [root@localhost linux]# ./a.out #等20秒退出 child exit code:10 [root@localhost linux]# ./a.out #在其他终端kill掉 sig code : 9
等待的本质:
监测子进程退出的信息
并且将子进程退出信息通过status拿回来
这个信息(exit code ,signal)在子进程的PCB中
再谈进程退出:
1.进程退出会变成僵尸进程->会把自己的退出结果写到自己的task_struct
2.wait/waitpid是一个系统调用->OS->OS有资格也有能力去读取子进程的task_struct
阻塞VS非阻塞
概念对比
例子:
1.不挂电话,监测李四的状态->阻塞
2.张三->李四,本质是状态检测,如果没有就绪,直接返回->每一次都是一次非阻塞等待->多次非阻塞等待我们称为:轮询
打电话->系统调用wait/waitpid
张三->父进程
李四->子进程
我们上面写的代码都是阻塞等待
轮询等待:
#include<sys/types.h>
#include<stdio.h>
#include<sys/wait.h>
int main()
{pid_t id = fork();if (id == 0){int cnt = 10;while (cnt){printf("我是子进程:%d,父进程:%d,cnt:%d\n", getpid(), getppid(), cnt--);sleep(1);}}int status = 0;while(1)//这是一个死循环,作为轮询等待的一个条件!{pid_t ret = waitpid(id, &status, WNOHANG);//wnohang:非阻塞:子进程没有退出时,父进程监测之后立即返回//如果没有设置WNOHANG,则会卡在这一步,除非子进程退出,或者你的id值设置得有问题.否则不会执行下面的代码if(ret == 0)//设置了WNOHANG才会可能出现返回值为0的情况{//waitpid调用成功&&子进程没有退出,我的waitpid没有等待失败//仅仅是监测到了子进程没有退出printf("wait done,but child is running\n",);}else if(ret > 0){//1.waitpid调用成功(等待成功)&&子进程退出了 printf("wait success: %d,sig number : %d ,child exit code: %d\n",ret,(status & 0x7F),(status>>8)&0xFF );break;}else{//waitpid调用失败,返回值为-1//eg.传入的id参数传错了printf("waitpid call failed\n");break;}sleep(1);}
}
非阻塞有什么好处
不会占用父进程的所有精力,可以在轮询期间干干其他的
[[指针进阶]]
[[typedef函数进阶]]
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <stdlib.h>#define TASK_NUM 10typedef void (*func_t)();
func_t other_task[TASK_NUM] = {NULL};void sync_disk()
{printf("这是一个刷新磁盘的任务\n");
}
void sync_log()
{printf("这是一个同步日志的任务\n");
}
void net_send()
{printf("这是一个网络发送的任务\n");
}void LoadTask(func_t func)
{for (int i = 0; i < TASK_NUM; i++){if (other_task[i] == NULL){other_task[i] = func;break;}}
}void InitTask()
{for (int i = 0; i < TASK_NUM; i++)other_task[i] = NULL;LoadTask(sync_disk);LoadTask(sync_log);LoadTask(net_send);
}void RunTask()
{for (int i = 0; i < TASK_NUM; i++){if (other_task[i] == NULL)continue;other_task[i]();}
}int main()
{pid_t id = fork();assert(id >= 0);if (id == 0){int cnt = 5;while (cnt){printf("我是子进程,我的pid是:%d,我的ppid是%d,我还有%dS存活\n", getpid(), getppid(), cnt--);sleep(1);}printf("我已经退出\n");exit(11);}InitTask();while (1){sleep(1);int status;pid_t ret_id = waitpid(id, &status, WNOHANG);if (ret_id > 0){printf("我是父进程,我已经回收子进程,ret_id是%d,退出码为%d,退出信号为%d\n", ret_id, (status >> 8) & 0xFF, status & 0x7F);exit(0);}else if (ret_id == 0){RunTask();continue;}else{printf("调用出错\n");}}
}
具体代码实现
进程的阻塞等待方式:
#define _CRT_SECURE_NO_WARNINGS 1
int main()
{pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);return 1;}else if (pid == 0) { //childprintf("child is run, pid is : %d\n", getpid());sleep(5);exit(257);}else {int status = 0;pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5Sprintf("this is test for wait\n");if (WIFEXITED(status) && ret == pid) {printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));}else {printf("wait child failed, return.\n");return 1;}}return 0;
}
运行结果:
[root@localhost linux] # . / a.out
child is run, pid is : 45110
this is test for wait
wait child 5s success, child return code is : 1.
进程的非阻塞等待方式:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);return 1;}else if (pid == 0) { //childprintf("child is run, pid is : %d\n", getpid());sleep(5);exit(1);}else {int status = 0;pid_t ret = 0;do{ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待->子进程没有退出,父进程检测时候,立即返回if (ret == 0) {//子进程没有退出,我的waitpid没有等待失败//仅仅是监测到了子进程没有退出printf("child is running\n");}sleep(1);} while (ret == 0);if (WIFEXITED(status) && ret == pid) {printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));}else {printf("wait child failed, return.\n");return 1;}}return 0;
}
让父进程做其他任务
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>#define TASK_NUM 10// 要保存的任务相关的
typedef void (*func_t)(); //?
func_t other_task[TASK_NUM] = {NULL}; //函数指针数组// 预设一批任务
void sync_disk()
{printf("这是一个刷新数据的任务!\n");
}
void sync_log()
{printf("这是一个同步日志的任务!\n");
}
void net_send()
{printf("这是一个进行网络发送的任务!\n");
}//将任务加载进任务列表
int LoadTask(func_t func)
{int i = 0;for(; i < TASK_NUM; i++){if(other_task[i] == NULL) break;}if(i == TASK_NUM) return -1;else other_task[i] = func;return 0;
}void InitTask()
{for(int i = 0; i < TASK_NUM; i++) other_task[i] = NULL;LoadTask(sync_disk);LoadTask(sync_log);LoadTask(net_send);
}void RunTask()
{for(int i = 0; i < TASK_NUM; i++){if(other_task[i] == NULL) continue;other_task[i]();}
}int main()
{pid_t id = fork();if(id == 0){//子进程int cnt = 50;while(cnt){printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid%d\n", cnt--, getpid(), getppid());sleep(1);//int *p = NULL;//*p = 100;}exit(111);}InitTask();// 父进程//pid_t ret_id = wait(NULL);while(1){int status = 0;pid_t ret_id = waitpid(id, &status, WNOHANG); // 夯住了if(ret_id < 0){printf("waitpid error!\n");exit(1);}else if(ret_id == 0){RunTask();sleep(1);continue;}else{if(WIFEXITED(status)) // 是否收到信号{printf("wait success, child exit code: %d\n", WEXITSTATUS(status));}else{printf("wait success, child exit signal: %d\n", status & 0x7F);}// printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d, child exit code: %d, child exit signal: %d\n",\// getpid(), getppid(), ret_id, (status>>8)&0xFF, status & 0x7F);break;}}
相关文章:

【Linux】进程等待
文章目录 进程等待进程等待必要性实验(见见猪跑)进程等待的方法wait方法waitpid**方法**宏的使用方法获取子进程status 阻塞VS非阻塞概念对比非阻塞有什么好处 具体代码实现进程的阻塞等待方式:进程的非阻塞等待方式:让父进程做其他任务 进程等待 进程等待必要性 之前讲过&am…...

电视「沉浮录」:跌出家电“三大件”?
【潮汐商业评论/原创】 “这年头谁还看电视,家里电视近一年都没打开过了,我明天就打算把它二手卖掉。”想到已落灰许久的电视机,Andy打开了二手平台。 “要不是这几年孩子网课多,我是真没考虑换新电视,家里用了8年的…...

前端实现调用打印机和小票打印(TSPL )功能
Ⅰ- 壹 - 使用需求 前端 的方式 点击这个按钮,直接让打印机打印我想要的东西 Ⅱ - 贰 - 小票打印 目前比较好的方式就是直接用 TSPL 标签打印指令集, 基础环境就不多说了,这个功能的实现就是利用usb发送指令,现在缺少个来让我们能够和usb沟通的工具,下面这就是推…...
串口通信(6)应用定时器中断+串口中断实现接收一串数据
本文为博主 日月同辉,与我共生,csdn原创首发。希望看完后能对你有所帮助,不足之处请指正!一起交流学习,共同进步! > 发布人:日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…...

【WinForm详细教程六】WinForm中的GroupBox和Panel 、TabControl 、SplitContainer控件
文章目录 1.GroupBox和Panel2.TabControl3.SplitContainer 1.GroupBox和Panel GroupBox:是一个分组容器,提供一个框架将相关的控件组织在一起,它有标题、边框,但没有滚动条。 Panel:也是一个容器控件,用来…...
gradle与maven
Gradle 和 Maven 都是流行的构建工具,通常用于构建和管理 Java 和 Android 项目。它们都可以自动下载依赖库、编译代码、运行测试、打包和发布等。 以下是对 Gradle 和 Maven 的介绍: Gradle: Gradle 是一个基于 Groovy 和 Kotlin 的构建自…...

2.Docker基本架构简介与安装实战
1.认识Docker的基本架构 下面这张图是docker官网上的,介绍了整个Docker的基础架构,我们根据这张图来学习一下docker的涉及到的一些相关概念。 1.1 Docker的架构组成 Docker架构是由Client(客户端)、Docker Host(服务端)、Registry(远程仓库)组成。 …...

拓世法宝 | 数字经济崛起,美业如何抓住流量风口?
爱美之心,人皆有之。无论男女,都会很自然地对美好事物燃起兴致,跟高颜值相关的事物总能聚集注意力。例如直播平台里的美女网红收割流量赚得盆满钵满,面庞俊俏的年轻偶像吸引万千粉丝,还有“央视最美记者”王冰冰、“最…...

Scala 泛型编程
1. 泛型 Scala 支持类型参数化,使得我们能够编写泛型程序。 1.1 泛型类 Java 中使用 <> 符号来包含定义的类型参数,Scala 则使用 []。 class Pair[T, S](val first: T, val second: S) {override def toString: String first ":" sec…...
索引失效的场景有哪些?
虽然你这列上建了索引,查询条件也是索引列,但最终执行计划没有走它的索引。下面是引起这种问题的几个关键点。 列与列对比 某个表中,有两列(id和c_id)都建了单独索引,下面这种查询条件不会走索引 select…...
Java进阶04 final关键字、abstract抽象、interface接口、JDK8与JDK9中接口的区别、内部类和匿名类
文章目录 一、final关键字二、abstract关键字三、接口interface四、JDK8和JDK9中接口的区别五、内部类 一、final关键字 final可以修饰类、方法、变量 用final修饰类 表示此类不能被继承 用final修饰方法 表示方法不可以被重写 用final修饰变量 既可以修饰成员变量也可以修饰…...

Python的web自动化学习(五)Selenium的隐式等待(元素定位)
引言: WebDriver隐式等待是一种全局性的等待方式,它会在查找元素时设置一个固定的等待时间。当使用隐式等待时,WebDriver会在查找元素时等待一段时间,如果在等待时间内找到了元素,则立即执行下一步操作;如果…...

20231102从头开始配置cv180zb的编译环境(欢迎入坑,肯定还有很多问题等着你)
20231102从头开始配置cv180zb的编译环境(欢迎入坑,肯定还有很多问题等着你) 2023/11/2 11:31 (欢迎入坑,本篇只是针对官方的文档整理的!只装这些东西你肯定编译不过的,还有很多问题等着你呢&…...
CentOS 安装HTTP代理服务器 Squid
参考:大部分摘自此文,做了少部分修改 Squid 是一个功能全面的缓存代理服务器,它支持著名的网络协议像 HTTP,HTTPS,FTP 等等。将 Squid 放在网页服务器的前端,通过缓存重复请求,过滤网络流量等&…...
ubuntu下开发提效的小tips
一、常用操作使用简写的别名,写进bashrc文件中 背景:经常需要cd至某个文件夹中,然后再执行对应的操作;写进bashrc文件中后,可以直接用缩略命令替代这一连串的命令; 用到的工具: 设置命令别名a…...

Java反射详解:入门+使用+原理+应用场景
反射非常强大和有用,现在市面上绝大部分框架(spring、mybatis、rocketmq等等)中都有反射的影子,反射机制在框架设计中占有举足轻重的作用。 所以,在你Java进阶的道路上,你需要掌握好反射。 怎么才能学好反射,我们需要…...

PostgreSQL 工具的相关介绍
1.1 psql工具 psql是PostgreSQL中的一个命令行交互式客户端工具,类似 Oracle中的命令行工具sqlplus,它允许用户交互地键入SQL语句或命 令,然后将其发送给PostgreSQL服务器,再显示SQL语句或命令的结 果。 1.2 psql的简单使用 使用…...

结合组件库实现table组件树状数据的增删改
如图所示,可以实现树状数据的新增子项,新增平级,删除。主要用到了递归 代码: <template><el-table :data"tableData" style"width: 100%; margin-bottom: 20px" row-key"id" border def…...

Microsoft 365 管理自动化
Microsoft 365 服务被大多数组织广泛使用,每天生成的数据量巨大。解决 Microsoft 365 中的问题可能非常困难,并且使用多个管理中心来保护组织变得复杂。本机控制台还缺少某些批量管理任务、全面的审计报告和基于角色的精细访问控制。 Microsoft 360 管理…...

unraid 安装并设置 zerotier 内网穿透安装 unraid 局域网内其他设备
Read Original 最近看了以下两个文章,感谢发布的各种精彩文章,让我受益匪浅。OPENWRT 的固件在设置了,【自动允许客户端 NAT】后,可以直接访问局域网其他设备,而我 unraid 部署 zerotier 后,只能访问 unra…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...