【Linux】线程Thread

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

线程概述

线程是进程中的一个执行单元

经典的并发模型,多进程模型,占用大量的内存开销,庞大的进程间调度开销,提供一种开销更小,更轻量级的并发解决方案,多线程技术。

线程在进程中,与进程共享资源和数据,进程是容器,线程是执行单元,进程退出可能导致所有线程退出。

每个进程中都有一个主控线程,还有其他的普通线程,操作系统会给进程分配需要的内存单元。

不支持线程技术的操作系统可以忽略线程概念,进程是调度单位。

多线程操作系统下,进程是内存管理单位,线程才是唯一的调度单元。

线程的CPU分配

Linux操作系统下,线程就是轻量级进程,每个进程分配一个LWP

在Linux操作系统下,所有的调度单位都是进程,淡化线程概念

线程的实现方式

用户线程(User Thread):在用户空间实现的线程,不是由内核管理的线程,是由用户态的线程库来完成线程的管理。可以利用第三方库的形式在不支持线程技术的系统下安装和使用线程,用户级线程的调度开销更小,因为大部分的操作都在用户层完成,无需内核干预

内核线程(Kernel Thread):操作系统中每创建一个线程,系统都会为其创建一个内核对象,此线程系统可以识别支持,会为线程执行资源(时间片)。Control控制器会为每个内核级线程创建内核对象,每个内核级线程都会由CPU进行时间片切换

轻量级进程(LightWeight Process):在内核中来支持用户线程;

进程的蜕化

进程内存资源独占,不与其他人共享,进程是独立的调度单位(无需考虑线程问题)

进程中出现了多个执行单元,讨论考虑线程问题

讨论和分析的蜕化,讨论线程,进程的讨论变为主线程和普通线程的分析和讨论

线程间的共享资源和非共享资源

PCB:共享资源

栈:线程栈空间非共享,每个线程创建后,会分配8MB线程栈

库:库资源共享

堆:共享堆空间

全局资源和静态资源:全局资源共享

代码段:代码段共享

文件描述符表:文件描述符表共享

信号的处理行为(某个线程改变信号行为)对所有线程共享。信号屏蔽字非共享,普通线程拥有独立的屏蔽字,拷贝继承于主线程

TCB:每个线程拥有自己的线程控制块,独立的tid

线程开发相关API接口

NPTL线程库

NPTL线程库是典型的内核级线程库,创建的线程可以被系统标识分配cpu资源(轻量级进程)

native Posix thread library

相关命令

ps aux 进程查看

ps ajx 进程关系查看

ps -eLf 所有线程查看 PID相同的是一组线程

LWPPID 相同的情况表示该进程只有一个线程。

ps -Lf pid 查看某一个进程下的线程

每个线程都会被系统分配lwp 调度编号, 便于系统管理调度线程,但是线程拥有独立的tid,线程id

头文件和函数

#include <pthread.h>使用线程函数的头文件

pthread_t tid线程tid类型

pthread_create

创建线程并指定类型pthread_create(pthread_t *tid,NULL,void * (thread_job)(void *),void *arg)第一个参数是线程id,第二个是线程属性,第三个参数是线程工作地址,第四个参数是线程工作参数。
返回值是err

下面是使用pthread_create创建线程的简单demo程序,通过PIDLWP相同可以看出,这个线程就是主控线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程被创建,参数为%d\n",*(int*)arg);return NULL;
}int main()
{pthread_t tid;int code=1024;//普通线程都是由主控线程创建的pthread_create(&tid,NULL,thread_job,(void*)&code);printf("普通线程被主控线程创建出来\n");while(1) sleep(1);return 0;
}

在编译时需要链接库

gcc Pthrad_Create.c -lpthread -o app

关于线程函数的错误处理

线程函数出错,会返回错误号(err>0),使用char *errstr=strerror(err)进行错误判断

下面是打印错误日志的demo程序:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程被创建,参数为%d\n",*(int*)arg);while(1) sleep(1);return NULL;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;//普通线程都是由主控线程创建的while(1){if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}else{printf("线程%d成功创建\n",++flags);}}return 0;
}

32位Ubuntu操作系统下,一个进程只能创建381个线程

pthread_self

pthread_tid =pthread_self(void) 成功返回当前线程的id

主线程通过pthread_create创建普通线程,成功传出tid与普通线程自身利用pthread_self()得到的tid值相等但是进程状态不相等,因为pthread_self()获取tid时可以保证当前的有效性,主线程在获取tid的时候,普通线程可能已经退出。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());return NULL;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);while(1) sleep(1);//主控线程还在运行,普通线程已经退出return 0;
}

pthread_join(pthread_t tid,void **retval)

线程回收函数,可以回收线程资源的同时获取线程的返回值。经典的阻塞回收函数,会一致等待普通线程结束后,进行回收。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());return (void*)126;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);pthread_join(tid,&revtal);printf("Thread Exit Code:%ld\n",(long int)revtal);return 0;
}

可以看到,如果线程工作函数可以正常退出,退出码就是线程工作函数的返回值

pthread_cancel(pthread_t tid)

指定线程tid,取消结束线程

下面是简单的取消一个线程的demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){printf("进程正在工作。。。\n");sleep(1);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();return 0;
}

线程被成功结束,这个进程下只有一个主控线程在工作

当我们对代码稍作修改:

删除线程工作函数中的调用系统函数:

void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();      return 0;
}

结果显示,pthread_cancel并没有成功取消线程。

这有些类似于信号,信号处理的三个切换条件:系统调用,软件中断,软件异常。而pthread_cancel取消事件的处理条件,必须有系统调用。

有一个专门为pthread_cancel提供系统调用的空函数:void pthread_testcancel(void),提供一次空的调用。

在线程工作函数加入这个函数再次尝试:

void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();      return 0;
}

可以看到只要一个主控线程,普通线程被结束了。

当我们对线程进行取消后,回收一下资源打印退出码看一下:

void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pthread_join(tid,&revtal);printf("Thread Exit Code:%ld\n",(long int)revtal);pause();      return 0;
}

可以看到退出码是-1,所以线程指定退出码时不允许使用-1,保留给pthread_cancel

pthread_detach(pthread_t tid)

用于将一个线程设置为分离态线程。

可以通过线程属性,批量创建分离态线程,这些线程诞生即是分离态。

线程有回收态和分离态,这两种状态是互斥的。

若修改线程退出状态,从回收态改为分离态,此操作不可逆,不能将分离线程变为回收态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{pthread_detach(pthread_self());while(1){pthread_testcancel();}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);if((err=pthread_join(tid,&revtal))>0){printf("join call failed %s\n",strerror(err));}printf("Thread Exit Code:%ld\n",(long int)revtal);pause();return 0;
}

joindetach会竞争时间片,所以有时线程以detach分离态被创建,当线程处于分离态时,无法手动回收,join执行失败

如果对一个分离态线程进行回收操作,pthread_join会失效返回。对一个已经处于回收阶段(join已经开始阻塞等待了)的线程设置分离,分离设置失败。

一个自行回收,一个由系统回收,一个可以得到线程退出码,一个无法获取

pthread_exit(void * exitcode)

线程退出函数,结束当前线程,与进程无关,退出码可以被join获取

线程的退出方式

return 0

主线程结束进程;结束普通线程,返回结果

pthread_cancel

主线程结束,与进程无关

普通进程结束,与进程无关

使用普通线程对主线程进行回收,产生了僵尸级线程

pthread_exit()

主线程结束,与进程无关

普通进程结束,与进程无关

exit()

进程退出,释放所有线程

线程属性

在使用创建线程函数时指定线程类型pthread_create(pthread_t *tid,pthread_attr_t *attr,void * (thread_job)(void *),void *arg)

用第二个参数指定线程属性

pthread_attr_t线程属性类型,是一个结构体类型

修改线程程属性的操作

使用pthread_attr_getdetachstate查看线程默认退出属性

使用pthread_attr_getstack查看栈属性


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);return 0;
}

使用pthread_attr_setdetachstate修改线程默认退出状态为分离态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>void *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_create(&tid,&attr,thread_job,NULL);int err=pthread_join(tid,NULL);if(err>0)printf("Join Call Failed:%s\n",strerror(err));return 0;
}

使用pthread_attr_setstacksize修改默认线程栈大小

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>void *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);stacksize=0x100000;int err;int flags=0;while(1){if((stack_addr=(void*)malloc(stacksize))==NULL){printf("malloc Stack Failed\n");}pthread_attr_setstacksize(&attr,stacksize);if((err=pthread_create(&tid,&attr,thread_job,NULL))>0){printf("Create failed:%s\n",strerror(err));exit(0);}printf("Thread %d 创建成功\n",++flags);}return 0;
}

由结果可知,当将默认大小变小后,32位系统下可以创建的线程数量增加了。

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

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

相关文章

【C语言】解决C语言报错:Double Free

文章目录 简介什么是Double FreeDouble Free的常见原因如何检测和调试Double Free解决Double Free的最佳实践详细实例解析示例1&#xff1a;重复调用free函数示例2&#xff1a;多次释放全局变量指针示例3&#xff1a;函数间传递和释放指针 进一步阅读和参考资料总结 简介 Doub…

nuget 包修改默认存放路径

平时使用 nuget packages 时&#xff0c;都是下载包文件到本地。 默认是在C盘&#xff0c;时间一久容量会高达几十个G&#xff0c;这样会拖慢系统运行效率。 这时需要修改包的下载位置。 打开nuget 包配置文件&#xff1a;Nuget.config 路径在 C:\Users\{UserName}\AppData…

网约车停运损失费:2、协商过程

目录 &#x1f345;点击这里查看所有博文 随着自己工作的进行&#xff0c;接触到的技术栈也越来越多。给我一个很直观的感受就是&#xff0c;某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了&#xff0c;只有经常会用到的东西才有可能真正记…

鸿蒙开发Ability Kit(程序框架服务):【FA模型切换Stage模型指导】 配置文件差异

配置文件的差异 FA模型应用在[config.json文件]中描述应用的基本信息&#xff0c;一个应用工程中可以创建多个Module&#xff0c;每个Module中都有一份config.json文件。config.json由app、deviceConfig和module三部分组成&#xff0c;app标签用于配置应用级别的属性&#xff…

探索 JQuery EasyUI:构建简单易用的前端页面

介绍 当我们站在网页开发的浩瀚世界中&#xff0c;眼花缭乱的选择让我们难以抉择。而就在这纷繁复杂的技术海洋中&#xff0c;JQuery EasyUI 如一位指路明灯&#xff0c;为我们提供了一条清晰的航线。 1.1 什么是 JQuery EasyUI&#xff1f; JQuery EasyUI&#xff0c;简单来…

从写下第1个脚本到年薪40W,我的测试开发心路历程!

对于任何职业来说&#xff0c;薪资始终都会是众多追求的重要部分。前几年测试行业还是风口&#xff0c;但是随着不断新鲜血液的加入&#xff0c;再加上就业大环境不好&#xff0c;企业也都在“降本增效”。目前内卷也是越来越激烈。不得不承认当下的现状&#xff0c;已经不仅仅…

Cadence计算器函数leafValue

与getData结合使用 leafValue( getData(“/output” ?result “dc”) 转自eetop https://bbs.eetop.cn/thread-931912-1-1.html

DM达梦数据库数学函数整理

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

期货交易记录20240626

文章目录 期货交易系统构建第一步、选品第二步、心态历练第三步、开仓纪律第四步、持仓纪律第五步、接下来的计划 2024年6月26号&#xff0c;开始写期货交易的第四篇日记。 交易记录&#xff1a;做了一笔纯碱的多单&#xff0c;在回撤了400个点左右后&#xff0c;看到企稳信号后…

记录正则提取文章

收到了个word版的电子书&#xff0c;需要拆分并转换为md存储到数据库中&#xff0c;便于搜索&#xff0c;记录下用正则提取文章的过程 word原文中有目录&#xff0c;可提取出目录后&#xff0c;在正文中根据目录来正则提取文章 正则的多行匹配 在匹配大量文章的时候&#xff…

Power BI 占比函数

1&#xff0c;普通层级结构占比 占比1 DIVIDE([sum_qty], CALCULATE([sum_qty],ALLSELECTED(Item[ITEM_CODE]))) //按照line为一个整理展示数据占比2 SWITCH( true(),ISINSCOPE(Item[ITEM_CODE]),DIVIDE([sum_qty], CALCULATE([sum_qty],ALLSELECTED(Item[ITEM_CODE]))), IS…

石油化工厂为什么要用专业防爆手机?

防爆手机之所以必须使用专业设计的产品&#xff0c;主要是出于安全考虑&#xff0c;以防止在易燃易爆环境中因手机使用不当引发爆炸事故。以下几点详细解释了使用专业化工防爆手机的必要性&#xff1a; 本质安全设计&#xff1a;顶坚专业防爆手机采用了本质安全&#xff08;本安…

植物大战僵尸杂交版技巧大全(附下载攻略)

《植物大战僵尸杂交版》为策略游戏爱好者带来了全新的挑战和乐趣。如果你是新手玩家&#xff0c;可能会对游戏中的植物和僵尸感到困惑。以下是一些实用的技巧&#xff0c;帮助你快速掌握游戏并享受其中的乐趣。 技巧一&#xff1a;熟悉基本玩法 游戏的基本玩法与原版相似&…

Java学习笔记(多线程):CompetableFuture

本文是自己的学习笔记&#xff0c;主要参考资料如下 https://www.cnblogs.com/dolphin0520/p/3920407.html JavaSE文档 https://blog.csdn.net/ThinkWon/article/details/102508721 1、Overview2、重要参数3、主要方法3.1、创建实例&#xff0c;获取返回值3.2、线程执行顺序相关…

Linux系统安装Lua语言及Lua外部库

安装Lua Lua语言是一种轻量级、高效且可扩展的脚本语言&#xff0c;具有简洁易学的语法和占用资源少的特点。它支持动态类型&#xff0c;提供了丰富的表达式和运算符&#xff0c;同时具备自动垃圾回收机制和跨平台性。Lua语言易于嵌入到其他应用程序中&#xff0c;并可与其他语…

查询DBA_TEMP_FILES报错,删除临时表空间报错ORA-60100

SYMPTOMS 查询DBA_TEMP_FILES报错如下图 ORA-01157: cannotidentify/ock data fle 201 -see DBWR trace fle ORA-01110: data fle 20 1: D:APPADMINISTRATORIORADATA MARTIDATAFILE 01157,00000-"cannotidentify/ock data fle %s -see DBWR trace fle"*Cause: The b…

安卓免费短剧大全v1.0.2/全部无需VIP实时更新全平台短剧

在当今社会&#xff0c;时间成为了许多人最为宝贵的资源。忙碌的工作与繁重的日常事务&#xff0c;常常让我们难以拨出时间沉浸于长篇大幅的影视作品中。对于那些热爱剧情、渴望在生活中点缀一抹戏剧色彩的朋友们而言&#xff0c;这无疑是一种挑战。 然而&#xff0c;随着免费…

高通安卓12-在源码中查找应用的方法

1.通过搜索命令查找app 一般情况下&#xff0c;UI上看到的APP名称会在xml文件里面定义出来&#xff0c;如 搜索名字为WiGig的一个APP 执行命令 sgrep "WiGig" 2>&1|tee 1.log 将所有的搜索到的内容打印到log里面 Log里面会有一段内容 在它的前面是这段内…

尚品汇-(七)

&#xff08;1&#xff09;在网关中实现跨域 全局配置类实现 包名&#xff1a;com.atguigu.gmall.gateway.config 创建CorsConfig类 Configuration public class CorsConfig {Beanpublic CorsWebFilter corsWebFilter(){// cors跨域配置对象CorsConfiguration configuration…

操作符详解(上) (C语言)

操作符详解&#xff08;上&#xff09; 一. 进制转换1. 二进制2. 二进制的转换 二. 原码 补码 反码三. 操作符的分类四. 结构成员访问操作符1. 结构体的声明2. 结构体成员访问操作符 一. 进制转换 1. 二进制 在学习操作符之前&#xff0c;我们先了解一些2进制、8进制、10进制…