- 博客(62)
- 收藏
- 关注
原创 进程间通信
1.原理:父进程创建文件描述符表,表里的指针指向结构体struct file,里面的属性指向inode和缓冲区等,创建子进程,子进程同时会拷贝文件描述符表,所以子进程和父进程指向同一个结构体,也就可以指向同一个文件,首先实现了不同进程看到同一份资源。子进程继承了父进程的文件描述符表,所以两个进程指向的管道是同一个,在没有名字的情况下可以保证是一条管道。• 进程控制:有些进程希望完全控制另⼀个进程的执行(如Debug进程),此时控制进程希望能够 拦截另⼀个进程的所有陷入和异常,并能够及时知道它的状态改变。
2025-12-11 00:00:00
260
原创 数据链路层
• 源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少”,并将这个请求广播 到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示广播)。• "以太网"不是一种具体的网络,而是一种技术标准,既包含了数据链路层的内容,也包含了一些物 理层的内容,例如:规定了网络拓扑结构,访问控制方式,传输速率等。• 注意到源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现⼀次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。
2025-12-10 02:00:00
871
原创 传输层UDP
在应用层里的数据都会序列化成字节流,然后把报文交给下一层,要交给下一层就得添加报头,添加udp报头,填写16位源端口,填写16位目的端口,16位UDP长度代表整个报文长度,16位UDP校验和用于校验,不成功丢弃。• UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致,如果缓冲区满了,再到达的UDP数据就会被丢弃。应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并,用UDP传输100个字节的数据。• 面向数据报:不能够灵活的控制读写数据的次数和数量。
2025-12-05 00:00:00
327
原创 传输层TCP协议
TCP引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。1.发一条回应一条效率太低,所以可以发多条回应多条,规定一次发多条这个最大值就叫做窗口大小,这个窗口叫滑动窗口,决定了无需确认等待一次发多条的最大值,这个滑动窗口在发送缓冲区里,是发送缓冲区的一部分,定义两个指针作为滑动窗口,窗口前面是已发送已确认的信息,后面是待发送的信息。• 另一方面,TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这⼀个连接,既可以读数据,也可以写数据,这个概念叫做全双工。
2025-12-04 00:00:00
873
原创 Linux信号中断
但是当没有中断来的时候,操作系统是暂停的,如果一直没有外部设备响应的话,操作系统就会一直暂停,于是在向量中断表当中注册一个进程调度的方法schedule,在硬件上引入时钟源,它会以特定频率向CPU发送特定的中断。按照以前的说法,键盘是硬件,操作系统就要管理这个键盘,然后定期去检查键盘是否有输入数据,有的话就把数据搬到内存里,然后放到键盘所对应的缓冲区里,struct file结构体指向缓冲区,唤醒进程,进程通过文件描述符就能把数据拿到用户空间。总而言之,信号是用软件来模拟硬件的信号中断的。
2025-12-03 00:00:00
430
原创 网络层ip协议
我们知道,IP地址(IPv4)是⼀个4字节32位的正整数,那么⼀共只有2的32次方个IP地址,大概是43亿左右,而TCP/IP协议规定,每个主机都需要有一个IP地址,这意味着,一共只有43亿台主机能接入网络么。因此大量的IP地址都被浪费掉了。可见,IP地址与子网掩码做与运算可以得到网络号,主机号从全0到全1就是子网的地址范围,IP地址和子网掩码还有一种更简洁的表示方法,例如140.252.20.68/24,表示IP地址为140.252.20.68,子网掩码的高24位是1,也就是255.255.255.0。
2025-12-02 00:00:00
596
原创 网络应用层
无状态比如不会记录是否访问了首页,只要想访问首页就得再请求,不会记录用户信息,登录一次后不会记录要再次登录,为解决这个问题,http提供了cookie报头,服务端和客户端,客户端发起request得到登陆页面,发起一个fork表单输入账号密码,然后后台认证,返回response,里面有setcookie,里面有用户信息,返回给了浏览器端,在浏览器内部保存起来,保存起来的这一小段信息叫做cookie,以后浏览器发送请求就把cookie一起发过去,这样服务器自动认证,推资源。
2025-12-01 02:30:00
678
原创 网络基础
现在主机A发送12个字节给主机B,操作系统是用C语言写的,所以双方的网络代码一样,左边有一个这样的结构体右边也有,主机B收到这12个字节然后强转为这个约定好的结构体类型就知道每个字段是什么含义,可以利用双方的结构体是什么类型这就是协议。定好协议,但是你用频率表示01,我用强弱表示01,就好比我用中国话,你用葡萄牙语⼀样,虽 然大家可能遵守的一套通信规则,但是语言不同,即是订好了基本的协议,也是无法正常通信的。TCP/IP通讯协议采用了5层的层级结构,每⼀层都呼叫它的下⼀层所提供的网络来完成自己的需求.
2025-11-27 00:00:00
839
原创 Linux信号量
信号量原子申请成功继续运行,申请失败申请的线程就被阻塞,数据信号量为0消费者P操作阻塞,生产者空格子多可以随时运行,所以可以继续P操作后接着运行直到N--到0信号量为0,再去P操作无法进行被阻塞,同时消费者那边的sem_data数据的信号量一直++P操作就会运行,然后就数据就去--直到为0,V操作空格子++生产者可以继续运行,这样就可以为空时生产者先运行,为满时消费者先运行且生产者不会多出消费者一轮。生产者:P操作申请资源,对空格子--,空格子被占用,多了数据就要V操作去对生产者的指针++
2025-11-26 00:00:00
160
原创 线程控制
Linux系统中不存在真正意义的线程,使用轻量级进程模拟的,OS中,只有轻量级进程,所谓模拟线程是我们的说法。但是用户只认线程,为了使用线程并且方便轻量级进程的封装,就有了pthread库,把创建轻量级进程的接口封装起来,给用户提供一批创建线程的接口。需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的, 不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
2025-11-24 00:00:00
709
原创 线程同步与互斥
谁有mutex的1谁就持有锁,申请锁,寄存器al先置0,然后mutex和al的值交换,al的值是1,就申请锁成功,如果线程切换,1会被带走。执行第二步时,线程切换,线程A保存临时数据,CPU内的数据可以被覆盖了,线程B去做--,减到一半又切换,变成A的线程,线程A的值写入内存。但是持有锁被切换,其他线程得等该线程执行完代码,释放锁,其他线程才能展开对锁的竞争,执行期间不会被打扰。大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量 归属单个线程,其他线程无法获得这种变量。
2025-11-24 00:00:00
312
原创 线程理解
2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多,最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。4kb划分是操作系统划分的,不管是磁盘还是内存。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指 的是增加了额外的同步和调度开销,而可用的资源不变。2.健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
2025-11-23 00:00:00
825
原创 线程id及进程地址空间布局
线程id,在库当中创建描述线程的线程控制块的起始地址,线程返回值是线程的退出结果写入void*ret里,线程分离,在线程控制块里有一个线程状态,int joinable默认=1,默认情况下joinable=1,还有一个joinable表明当前线程是不是被分离的,设置分离就是将able设为0,线程库自动把对应的控制块释放掉。2.调用pthread_creat函数要在内核当中创建轻量级线程,用系统调用创建,告诉系统调用执行什么方法,栈在哪里,调用的系统调用底层称为克隆,用于创建轻量级线程。
2025-11-23 00:00:00
232
原创 Linux信号
信号发给进程,在PCB里定义一个unsigend int sigs,用整数记录这个信号,但如果是要发好多个,一个整数不够记录,就把这个整数当作位图结构,一共32个比特位,规定比特位的位置表示信号编号,比特位的内容表示是否收到,比如收到2号信号,就把第二位比特位由0置1,记录在进程的task_struct里。kill底层调用了系统调用。一、预备:中断当前正在做的事情,是一种事件的异步通知机制,当前正在做的事情为进程,信号到来时进程就要中断正在做的事情,信号就是一种给进程发送的用来进行事件的异步通知机制。
2025-11-22 00:00:00
1067
原创 缓冲区io
读写文件时,如果不会开辟对文件操作的缓冲区,直接通过系统调用对磁盘进行操作(读、写等),那么每次对文件进行一次读写操作时,都需要通过读写系统调用来处理此操作,即需要执行一次系统调用,执行一次系统调用将涉及到CPU状态的切换,即从用户空间切换到内核空间,实现上下文的切换,这将损耗一定的CPU时间,频繁的磁盘访问对程序的执行效率造成很大影响。缓冲区是内存空间的一部分,也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,缓冲区根据其对应的。
2025-11-21 00:00:00
571
原创 动静态库
这个时候,动态链接的优势就体现出来了,我们可以将需要共享的代码单独提取出来,保存成⼀个独 ⽴的动态链接库,等到程序运⾏的时候再将它们加载到内存,这样不但可以节省空间,因为同⼀个模 块在内存中只需要保留⼀份副本,可以被不同的进程所共享。如果不进行合并, 假设页面大小为4096字节(内存块基本大小,加载,管理的基本单位),如果.text部分为4097字节,.init部分为512字节,那么它们将占用3个页面,而合并后,它们只需2个页面。1.库是写好的现有的,成熟的,可以复⽤的代码。库有两种:静态库和动态库。
2025-11-21 00:00:00
842
原创 文件操作
在进程的PCB当中,当一个进程被创建,要创建一个struct file_struct,被称为文件描述符表,文件描述符表中有一个可变数组,叫做 struct file* fd_array[ ] ,是一个指针数组,PCB里有一个 struct file_struct* file 指针,指向文件描述符表,数组里面放的是该进程打开的文件。所以需要将a的数字格式化为字符串。每一个文件提供一个文件缓冲区,由于文件=内容+属性,结构体在操作系统内部申请一段缓冲区,文件的内容加载到缓冲区中,文件的属性就用来初始化结构体。
2025-11-20 00:00:00
1835
原创 EXT系列和文件系统
fopen打开文件就在查找路径,找到文件名和inode的对应关系,根据路径依次搜索找到文件的inode再找到文件的内容,在内核当中创建struct file、inode和文件缓冲区,创建文件操作的文件描述符表然后填充,磁盘属性加载到内存,文件内容加载到文件缓冲区里,然后把struct file对应的地址在文件描述符表中进行分配,返回文件描述符。Linux中,任何文件都要有自己的属性集合,用结构体来表示,这个结构体就叫做inode,大小固定,一般是128字节,所有文件属性大小最多128kb。
2025-11-19 00:00:00
554
原创 进程地址空间
但是一个进程会有无数个虚拟地址,每个虚拟地址映射到物理内存对应的功能也是不一样的,比如有点虚拟地址里面的内容是变量,有的虚拟地址是用来进行代码功能的实现,所以这些虚拟地址就需要被管理起来划分成一个个区域,管理就是用结构体先描述再组织,这个被划分的区域就是进程地址空间,所以进程地址空间本质是一个结构体,这个结构体叫做mm_struct,每个进程只有一个mm_struct,每个进程的tast_struct都有一个指向mm_struct结构体的指针。子进程的页表拷贝自父进程的页表,包括内容,发生了简单的浅拷贝。
2025-11-18 00:00:00
318
原创 环境变量与地址
argv[]是个变长数组。比如要导入一个环境变量叫MYENV=11223344,就写export MYENV=11223344,这样就可以让系统在环境变量表里写入MYENV字符串了,然后再查找,就会出现MYENV=11223344。方法一:int argc, char *argv[], char *env[],后面两个是命令行参数表和环境变量表,是由系统解析好之后传给main函数的,所以可以在main函数里遍历env来获取环境变量。二、如何理解环境变量:登录的时候形成一张表,叫环境变量表,是一个指针数组。
2025-11-17 00:00:00
519
原创 进程等待
返回值:当正常返回的时候 waitpid 返回收集到的子进程的进程 ID,如果设置了选项 WNOHANG, 而调用中 waitpid 发现没有已退出的子进程可收集 , 则返回 0,如果调用中出错 , 则返回-1, 这时 errno 会被设置成相应的值以指示错误所在。1.wait:status参数是个输出型参数,需要传整型变量的地址,把子进程这个退出信息给父进程拿到,wait接口等待任意一个退出的子进程,返回目标僵尸进程的退出码。• 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
2025-11-16 22:27:25
338
原创 进程优先级
active指向的queue[140]调出要进行的进程到CPU完成之后就调回expired里的queue[140],调度过的进程都要到过期队列里,调度的过程中活跃队列里进程会越来越少,过期队列里会越来越多。如何切换:当兵:学校就叫CPU,辅导员叫做调度器,你是进程,学籍就是进程运行的临时数据,CPU寄存器内的临时数据,保留学籍,保留上下文数据,CPU寄存器内的数据保留起来,去当兵,就是进程被从CPU上剥离下来。竞争:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。
2025-11-16 00:00:00
465
原创 进程状态
僵尸进程:在Linux系统里,所有进程都是某一个进程的子进程,创建这些子进程是要去完成某种事情的,比如shell创建子进程是为了执行指定命令,也就是说子进程是为了完成父进程一部分工作的,完成之后就一定要返回工作完成的结果。假设CPU正在运行进程,需要读取设备,操作系统就会去查询设备的状态,假如不活跃,就无法执行。如果无法执行,操作系统就会把该进程从CPU里面的运行队列移出,再把这块被移出的task_struct链入特定设备的等待队列当中,此时该进程不在调度队列当中,就不会被调度了,此时就处于阻塞状态。
2025-11-15 00:00:00
578
原创 进程基本概念
proc记录的是当前系统里所有进程的信息,每个数字目录都是进程的pid,每个数字目录的内容都包含这个进程在运行时的动态属性,进程要是运行结束就会自动从proc这里移除,如果再启动就会出现在proc里。操作系统为程序创建一个结构体类型的对象,将信息填好就有了对应的一个节点,每一个节点都有自己的指针就可以指向自己对应的代码和数据,程序的所有属性都可以在自己的节点里找到,在操作系统内就形成了一个加载到内存当中的程序列表,叫做进程列表。未来要获取操作系统内部的数据,设置自己的信息,最终全部都是用系统调用完成的。
2025-11-14 00:00:00
1103
原创 在Linux系统下写一个进度条(命令行版本)
如果将\n给去掉,运行程序,但屏幕并不出现hello world,而是3秒后程序运行结束再出现,这并不是因为程序先执行了sleep(3),而是因为休眠的这3秒内这段字符串临时保存在缓冲区里,程序结束时会自动刷新缓冲区才会在3秒后出现hello world。而因为有了\n,它就会立马进行 行刷新。光标的组成有 \ | — / ,要让这几个字符在同一个位置循环出现,就得先定义一个数组存放这几个符号,然后求数组的长度,最后用cnt去模上这个长度让这个字符数组一直在0、1、2、3之间循环,这样就实现了旋转光标。
2025-06-05 15:38:39
704
原创 Linux项目自动化构建工具——make/Makefile
如果修改了code.c的内容,modify时间就更新,就比可执行程序要新,系统就意识到code文件是旧的,就会重新编译。在这个过程中,makefile从上到下扫描,要编译出code就要现有code.o,但是没有code.o,这条命令就先入栈,再到下一条命令,要编译出code.o就要有code.s,但是没有code.s,这条命令也入栈,跳到下一条命令,依次类推直到找到code.c可以编译出code.i,上面的命令依次出栈,就编译出了可执行程序code。对上面的变量做一下调整,为了可以生成 .o 的文件。
2025-06-05 15:35:02
571
原创 Linux编译器——gcc/g++的使用
进入cde.i,文件由原来的几行变成了好几百行,这是因为头文件在系统当作自带,打开之后发现有九百多行。根本原因就是因为头文件展开,将需要的头文件拷贝到目标文件形成 .i 文件,头文件里面也有它自己的头文件,就会进行不断地递归展开,所以就会变大。cde.o:可重定位目标文件,在win和vs就叫做 .obj。我们的源文件中会包含很多库方法,此时cde.o就已经是一个二进制文件了,但我们的库方法没有和程序联系起来,所以程序无法进行,就需要链接才能形成可执行文件。-c:从现在开始程序开始翻译,直到汇编完成停下。
2025-05-27 16:24:04
231
原创 Linux编辑器——vim的使用
基本操作:vim打开默认是命令模式,也就是输入命令然后系统执行指令,想要写代码,只需输入字母i,就进入插入模式,写完代码想要退出,按一下Esc,退回到命令模式,然后在输入冒号,进入底行模式,输入w保存,输入q退出。批量化注释:ctrl+v进入视图模式,然后使用hjkl进行区域选中,无法使用键盘的上下左右键,选中完成后输入shift+i也就是大写I,进入插入模式,然后输入//进行注释然后esc退出,就可以在选择的区域同时写入//。vim+src+n:可以快速将光标定位到src文件的第n行。
2025-05-27 16:23:45
1034
原创 Linux权限
当前我的系统默认的权限掩码是0002,这个数字是八进制的,第一个0是与用户相关的,可以不做考虑,也就是说我的权限掩码缺省的是002,写成二进制就是000 000 010,它的意思是文件的最终权限=起始权限 & (~umask)。以目录权限为例,起始权限是777转换为二进制就是111 111 111,umask就是000 000 010,对其进行按位取反,就变成111 111 101,再与起始权限按位与,就是111 111 101,这个数字转换为八进制就是775也就是目录所显示的最终权限。
2025-05-21 16:23:02
542
原创 C++之多态
然后再看main函数,p是B对象类型的指针,所以调用test时就把B类型对象的指针传给了test里面的this指针,但是这里的this是A*类型的,因为调用的是A里面的test(),所以这时候就是A*指向了B对象,所以接下来就是A*this->func(),由于this指向的是B对象,func还被重写了,所以构成多态,指向谁调用谁,所以就调用B里面的函数。但是要注意这里继承下来的基类部分的虚函数表指针和基类对象的虚函数表指针不是同一个,就像基类对象的成员和派生类对象中的基类成员对象也是独立的。
2025-03-14 21:36:19
759
原创 C++之继承
注意使用父类函数时模板必须得在前面加上模板类的声明vector<T>::,因为在调用stack时,编译器会按需实例化会先调用构造函数,这时候模板的参数T就定了下来,假如这个参数实例化成int类型的,但是push_back里面的T没有定下来,最好调用stack.push()时编译器在自己类找不到vector<int>,在父类中也找不到,就会报错,如果说明了是vector<T>::,编译器就知道这个T是int类型的。(2)父类和子类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏。
2025-03-14 20:59:30
943
原创 C++之模板进阶
编译链接的过程:预处理进行头文件展开/宏替换/条件编译/去掉注释生成func.i和test.i,然后就去检查语法,生成汇编代码,生成func.s和test.s然后再把汇编代码转换为二进制机器码分别产生func.o和test.o,这些.o文件都有个符号表,里面有对应的函数名后面跟着对应的地址,最后两个文件去链接,将目标文件合并在一起生成可执行程序,并且把需要的函数的地址链接上。用函数模板既可以比较整型类型也可以比较类类型对象,但是无法比较指针类型的,因为传过去的是对象的地址,最后比较的是对象地址的大小。
2025-03-11 20:25:52
826
原创 C++之list
(3)迭代器实现:由于链表在内存内部是不连续的,所以用迭代器的指针无法去直接++或者--,想要实现迭代器的基本功能必须对其他的运算符进行重载。(4)排序:sort()默认是升序,要实现降序可以使用仿函数greater<int> ls,less<int> ls用于升序。首先是重载*,需要返回内部的值,++则是返回下一个节点,--则是返回前一个节点。(5)两个链表合并:使用merge函数传合并的对象,最后传的对象内部会空。(7)剪切:有两个链表对象1和2,将2的值剪切到1里面去,最后2会空。
2025-03-09 19:52:29
613
原创 C++之vector
但是这样写是错误的,程序会崩溃,因为一开始扩容的时候vector里的空间是0,那么_start和_finish指向的一块空间,大小是一样的,所以size()返回的值是0,实现size()就是为了返回原来的相对位置好对_finish进行修改,但是先把tmp赋值给_start将会导致size()传的是已经改变了位置的_start和不变的_finish直接的相对位置,就会发生指针越界的情况导致程序崩溃。因为传入的数据不需要修改,所以用const修饰,如果是string类型的,为了减少拷贝构造的调用次数就传引用。
2025-03-01 17:58:28
1024
原创 C++之string类
nops是缺省值,该接口是为了拷贝string的一部分,nops是string的const静态的成员变量,值为-1,但其实是把-1给了一个无符号的len瞬间就变成整型的最大值了,42亿九千万字节。1.string类的重要性:C语言中,字符串是以“\0”结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OPP的思想,而且底层空间需要用户自行管理,稍不留神可能会越界访问。string是一个对象,使用字符的顺序表实现的,就是一个字符顺序表。
2024-11-03 23:06:53
393
原创 C++模板初阶
2.3 原理:函数模板本身是一个蓝图,不是函数,是编译器用使用方法产生特定具体类型的模具,其实模板就是将本来应该我们做的重复的事情交给了编译器,将实参传递给形参,编译器再推出形参具体的类型,模板不能调用,是用模板生成了具体的函数。真正让C++突飞猛进的就是模板,有时候要实现一个交换变量的函数,但是如果是不同类型的变量就要写不同的交换函数,就很麻烦,这时候就需要模板来简化这个过程,写一个函数就可以兼容其他类型的,类似于活字印刷。2.4 模板的实例化:用不同类型参数使用函数模板时称为模板的实例化。
2024-10-23 15:38:08
315
原创 C/C++内存管理
5.1 如果申请的是内置类型的空间,new和malloc、delete和free基本类似,不同的是new和delete申请和释放的是单个元素的空间,new[ ] 和 delete[ ] 申请的是连续的空间,而new在申请失败后会报异常,malloc会返回NULL。*申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造和析构函数,而new在申请空间后会调用构造函数完成对对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放。在申请的空间上执行构造函数,完成对象的构造。
2024-10-18 11:30:17
909
原创 类和对象(下)
尽量使用初始化列表进行初始化,因为那些不在初始化列表初始化的成员也会走初始化列表的途径,如果这个成员在声明位置给了缺省值,初始化列表也会用这个缺省值初始化。*内部类本质也是一种封装,当A类和B类紧密关联时,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属类,其他地方用不了。*初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表中出现的先后顺序无关,建议声明顺序和初始化列表顺序保持一致。
2024-10-15 23:41:28
1042
原创 类和对象(中)
d1.Print(),这里取d1地址的类型传过去,d1地址类型是const Date*,指向内容不能修改,但是Print用来接收的指针是Date* const this,const修饰的是指针本身,指针本身不能修改而已不是里面的内容不能修改,所以权限放大了,就不行。1赋值给k,k作为返回值赋值给j,j再返回赋值给i。所以在完成d2=d3拷贝构造时,最后要把d2的值返回然后赋给d1,由于d3传给了d的别名,所以d2传给了this指针,这个指针里面放的是d2的地址,所以就可以返回this的解引用。
2024-10-04 00:03:11
1121
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅