C ++中的—>和 . 的区别

本文详细解释了C++中类的成员变量如何通过对象和指针进行访问,包括实例化类、对象成员操作及指针成员操作的区别与用法。

比如C++中一个类Class Student;里面有个公有成员变量test

如果你声明这个类的对象Student s;那么你要对test操作要用:s.test

如果你声明这个类的对象Student *s为指针类型的话,那么你要对test操作要用:s->test

或者

如果一个指针型变量指向一个类/结构体的实例,该指针访问实例中的成员时,要使用->,但如果这个实例本身要访问自身的成员,就使用. 举个例子: 
struct Test{ int i;int j;}; 

struct Test hh; //实例化
//注意使用.的地方哦:
hh.i = 10;
hh.j = 100;
sturct Test* p = &hh;
//注意:下面使用-> 
p->i = 100;
p->j = 1000;





xv6的malloc函数(在文件user/umalloc.c中)实现了堆空间的分配,请问它采用的是什么空闲内存分配策略?请描述其具体做法,代码如下: #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #include "kernel/param.h" // Memory allocator by Kernighan and Ritchie, // The C programming Language, 2nd ed. Section 8.7. typedef long Align; union header { struct { union header *ptr; uint size; } s; Align x; }; typedef union header Header; static Header base; static Header *freep; void free(void *ap) { Header *bp, *p; bp = (Header*)ap - 1; for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) break; if(bp + bp->s.size == p->s.ptr){ bp->s.size += p->s.ptr->s.size; bp->s.ptr = p->s.ptr->s.ptr; } else bp->s.ptr = p->s.ptr; if(p + p->s.size == bp){ p->s.size += bp->s.size; p->s.ptr = bp->s.ptr; } else p->s.ptr = bp; freep = p; } static Header* morecore(uint nu) { char *p; Header *hp; if(nu < 4096) nu = 4096; p = sbrk(nu * sizeof(Header)); if(p == (char*)-1) return 0; hp = (Header*)p; hp->s.size = nu; free((void*)(hp + 1)); return freep; } void* malloc(uint nbytes) { Header *p, *prevp; uint nunits; nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; if((prevp = freep) == 0){ base.s.ptr = freep = prevp = &base; base.s.size = 0; } for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ if(p->s.size >= nunits){ if(p->s.size == nunits) prevp->s.ptr = p->s.ptr; else { p->s.size -= nunits; p += p->s.size; p->s.size = nunits; } freep = prevp; return (void*)(p + 1); } if(p == freep) if((p = morecore(nunits)) == 0) return 0; } }
最新发布
11-20
<think> 我们正在分析xv6的user/umalloc.c中malloc函数的空闲内存分配策略。根据提供的上下文代码跟踪,我们可以总结如下: 1. **策略类型**:xv6的用户态内存分配器(在`umalloc.c`中)采用**显式空闲链表(explicit free list)**策略。具体来说,它使用一个全局指针`freep`指向一个循环链表,链表中的每个节点代表一个空闲内存块。 2. **内存块结构**:每个空闲块已分配块都以一个头部(`Header`)开始,其结构定义如下: ```c typedef union header { struct { union header *ptr; uint size; } s; // 用于对齐(最小分配单位) } Header; ``` 其中: - `ptr`字段指向链表中的下一个空闲块。 - `size`字段表示该空闲块的单位数(每个单位大小为`sizeof(Header)`,即16字节)。 3. **分配策略**: - **首次适应(First Fit)**:当分配内存时,分配器从`freep`开始遍历空闲链表,找到第一个大小满足要求的空闲块。 - 如果找到的空闲块恰好满足需要(即空闲块大小等于请求大小加上一个头部的单位数),则将其从链表中移除并返回给用户。 - 如果找到的空闲块大于所需,则进行分割:分配器从该空闲块中分割出所需大小的块(包括头部),并将剩余部分作为新的空闲块留在链表中。 4. **释放策略**: - 释放内存时,分配器将已分配块转换为空闲块,并将其插入到空闲链表中(插入点在`freep`指向的块之前,以保持循环链表结构)。 - 在插入过程中,分配器会检查新释放的块是否与相邻的空闲块在地址上连续。如果连续,则进行合并(合并相邻的空闲块),以防止内存碎片。 5. **初始化**: - 初始时,堆空间由一个大的空闲块组成。这个空闲块通过`morecore`函数(内部调用`sbrk`系统调用)从内核申请而来。 - `base`是一个哨兵节点,用于简化链表操作。初始时,`freep`指向`base`,`base`指向自己,形成一个自循环链表。 6. **关键函数**: - `malloc`:实现内存分配。 - `free`:实现内存释放。 - `morecore`:当空闲链表没有足够大的块时,向内核申请更多的内存(通常是4096字节的整数倍)。 ### 具体实现步骤(以`malloc`为例) 1. **计算所需单位数**:将请求的字节数转换为以`Header`为单位的数量(每个单位16字节)。计算方式为: ```c nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1; ``` 这里加1是因为返回给用户的内存块也需要一个头部。 2. **遍历空闲链表**:从`freep`开始,循环查找一个大小至少为`nunits`的空闲块(首次适应)。 3. **分割或分配整个块**: - 如果找到的块大小正好等于`nunits`,则将其从链表中移除。 - 如果找到的块大于`nunits`,则分割:将块的前`nunits`个单位切分出来作为分配块,剩余部分形成一个新的空闲块(调整大小指针)。 4. **更新链表指针**:调整链表指针,将分割后的剩余块(或移除已分配块后的链表)重新链接。 5. **如果没有找到合适块**,则调用`morecore`申请更多内存,然后重新搜索。 ### 示例分析(来自跟踪脚本) 在跟踪脚本中,我们观察到: - 第一次`malloc(0x1000)`后,剩余的空闲块链表有两个节点:节点1(`0x1000`,长度0)节点2(`0x4000`,长度`0xeff`个单位)。 - 注意:节点1(`0x1000`)是`base`节点,它是一个哨兵节点,大小为0,用于链表循环。 - 节点2是实际可用的空闲块,其大小为`0xeff`个单位,即`0xeff * 16 = 0xeff0`字节(包括头部)。 在第三次`malloc(0x100)`(即256字节)后: - 分配器会从空闲块(节点2)中分割出一部分。因为256字节需要`(256+15)/16 + 1 = 17+1=18`个单位(每个单位16字节)。 - 分割后,节点2的大小减少18个单位,新的空闲块起始地址为原节点2的地址加上18个单位(即`0x4000 + 18*16 = 0x4000 + 0x120 = 0x4120`),大小变为`0xeff - 18 = 0xee7`个单位。 ### 总结 xv6的用户态内存分配器(`umalloc.c`)采用**显式空闲链表****首次适应**分配策略,并支持相邻空闲块的合并以降低碎片。其设计相对简单,适合教学使用。 §§相关问题§§ 1. 显式空闲链表与隐式空闲链表在实现上有何区别? 2. 首次适应、最佳适应最坏适应策略各有什么优缺点? 3. 内存分配器如何避免碎片问题? 4. `sbrk`系统调用在xv6中是如何实现的? </think> ### xv6 用户态内存分配器(umalloc.c)的空闲内存分配策略 xv6 的 `user/umalloc.c` 实现的 `malloc` 函数采用 **显式空闲链表(Explicit Free List)** 策略,结合 **首次适应算法(First Fit)**。具体实现机制如下: #### 1. 内存块结构 ```c typedef union header { struct { union header *ptr; // 指向下一个空闲块的指针 uint size; // 当前空闲块的大小(以Header为单位) } s; // 用于内存对齐(最小分配单位) } Header; ``` - 每个内存块(包括空闲块已分配块)都以 `Header` 开头 - `size` 字段表示块的单位数(1单位 = `sizeof(Header)` = 16字节) - `ptr` 字段仅在空闲块中有效,指向链表中的下一个空闲块 #### 2. 全局数据结构 ```c static Header base; // 哨兵节点(链表起点) static Header *freep = 0; // 指向空闲块链表的指针 ``` - 使用 **循环链表** 组织空闲块 - `base` 作为哨兵节点始终存在,大小为0 - `freep` 始终指向当前扫描位置,优化搜索效率 #### 3. 分配策略(malloc) ```c void* malloc(uint nbytes) { Header *p, *prevp = freep; uint nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; // 首次遍历:查找足够大的空闲块 for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) { if (p->s.size >= nunits) { // 找到合适块(首次适应) if (p->s.size == nunits) prevp->s.ptr = p->s.ptr; // 整块分配 else { // 分割块(p指向剩余部分) p->s.size -= nunits; p += p->s.size; p->s.size = nunits; } freep = prevp; return (void*)(p + 1); // 返回用户可用地址 } if (p == freep) // 遍历完整个链表 if ((p = morecore(nunits)) == 0) return 0; // 申请新内存 } } ``` - **首次适应算法**:顺序扫描空闲链表,选择 **第一个满足大小要求** 的空闲块 - **分割机制**: - 当空闲块大于需求时,将块分割为两部分 - 分配后半部分(地址更高),保留前半部分作为新空闲块 - **内存申请**:当空闲链表不足时,调用 `morecore()` 通过 `sbrk()` 系统调用向内核申请新内存页(通常为4096字节) #### 4. 释放策略(free) ```c void free(void *ap) { Header *bp = (Header*)ap - 1; // 指向块头 Header *p; // 寻找插入位置(地址有序) for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) break; // 合并相邻空闲块 if (bp + bp->s.size == p->s.ptr) { // 与后块合并 bp->s.size += p->s.ptr->s.size; bp->s.ptr = p->s.ptr->s.ptr; } else bp->s.ptr = p->s.ptr; if (p + p->s.size == bp) { // 与前块合并 p->s.size += bp->s.size; p->s.ptr = bp->s.ptr; } else p->s.ptr = bp; freep = p; // 更新扫描起点 } ``` - **地址有序插入**:将释放的块按地址顺序插入链表 - **相邻块合并**:检查前后相邻块是否空闲,若空闲则合并成大块(解决碎片问题) - **即时合并**:释放时立即执行合并操作,保持链表高效 #### 5. 策略特点 1. **显式链表优势**: - $O(1)$ 时间释放内存 - 减少分配时的搜索范围(相比隐式链表) 2. **首次适应特点**: - 实现简单,搜索速度快 - 易产生低地址碎片(可通过分割缓解) 3. **分割策略**: - 最小分配单位:16字节(Header大小) - 分割阈值:剩余空间需能容纳最小空闲块(2个Header) 4. **合并机制**: - 双向合并(前向+后向) - 合并后块大小 = $size_1 + size_2$ > 此设计在xv6教学系统中平衡了实现复杂度性能[^1],实际系统(如Linux)使用更复杂的分配策略(如ptmalloc的bins机制)[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值