目录
[2.C 语言程序占用的内存分为哪几个部分](#2.C 语言程序占用的内存分为哪几个部分)
1.LRU缓存
cpp
class LRUCache {
public:
LRUCache(int capacity) : _capacity(capacity){
}
int get(int key) {
// 先判断_mmap是否已经存在该key值
if(_mmap.find(key) != _mmap.end())
{
// 1. 通过_mmap获取该key值在链表的位置
auto iter = _mmap[key].second;
// 2. 更新该元素的位置, 并返回value值
_list.splice(_list.begin(), _list, iter);
return _mmap[key].first;
}
return -1;
}
void put(int key, int value) {
// 先判断_mmap是否已经存在该key值
if(_mmap.find(key) != _mmap.end())
{
// 若存在
// 1. 直接在链表中更新该元素的位置
auto iter = _mmap[key].second;
_list.splice(_list.begin(), _list, iter);
// 2. 再更新对应的value值
_mmap[key].first = value;
}
else
{
// 若不存在
// 1. 先构建pair对象
auto iter = _list.insert(_list.begin(), key);
std::pair<int, std::list<int>::iterator> pa(value, iter);
// 2. 插入到_mmap中
_mmap.emplace(key, pa);
// 3. 判断是否超出容量
if(_list.size() > _capacity)
{
// 若超出, 就删除最久没使用的对象
_mmap.erase(*_list.rbegin());
_list.pop_back();
}
}
}
private:
int _capacity;
std::unordered_map<int, pair<int, std::list<int>::iterator>> _mmap;
std::list<int> _list;
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
a.核心思想
当缓存容量达到上限时,优先淘汰最久未被使用的数据。
b.思路
**① 快速查找:**通过哈希表实现,用于快速判断某个键是否存在以及获取对应的值。
**② 维护访问顺序:**通过双向链表实现,最近访问的节点移动到链表头部,最久未访问的节点在链表尾部。
**③ 容量控制:**当缓存超过容量时,删除链表尾部的节点,并在哈希表中移除对应的键。
c.步骤
① 数据结构选择:
A. 使用
unordered_map存储键值对,键为缓存的键(传入的key值),值为缓存的值和指向双向链表节点的迭代器(传入的value值)。B. 使用双向链表(
list)维护访问顺序,链表头部是最近访问的节点,尾部是最久未访问的节点。② 核心操作:
A.
get(key):
如果键存在,将对应的节点移动到链表头部,并返回值。
如果键不存在,返回
-1。B.
put(key,value):
如果键存在,更新值,并将节点移动到链表头部。
如果键不存在,在链表头部插入新节点,并在哈希表中记录。
如果插入后超过容量,删除链表尾部的节点,并在哈希表中移除对应的键。
d.注意
cpp
int get(int key) {
if(_mmap.find(key) != _mmap.end())
{
// _list.erase(_mmap[key].second);
// _list.remove(key);
// _list.emplace_front(key);
auto iter = _mmap[key].second;
_list.splice(_list.begin(), _list, iter);
return _mmap[key].first;
}
return -1;
}
void put(int key, int value) {
if(_mmap.find(key) != _mmap.end())
{
// _list.remove(key);
// _list.emplace_front(key);
auto iter = _mmap[key].second;
_list.splice(_list.begin(), _list, iter);
_mmap[key].first = value;
}
else
{
auto iter = _list.insert(_list.begin(), key);
std::pair<int, std::list<int>::iterator> pa(value, iter);
_mmap.emplace(key, pa);
if(_list.size() > _capacity)
{
_mmap.erase(*_list.rbegin());
_list.pop_back();
}
}
}
这里一开始在编写代码时,犯了一个低级的错误,就是注释代码中的逻辑!
对于更新最新访问的节点,我的想法是直接删除这个元素,接着头插到链表就好了,但是这里存在一个致命的问题,那就是
_mmap并不知道我删除并重新插入该元素,所以_mmap里保存的iterator仍然是被删除的元素的迭代器,此时该迭代器失效,导致后续再使用该迭代器时会报错。(要不就一起改,要不就使用splice)另外即使一起改了,也大概率编不过,因为效率太低了。
2.C 语言程序占用的 内存 分为哪几个部分
a.代码区
存储内容:编译后的机器指令(即程序的二进制代码)
特点:只读、固定大小、共享性
cpp
int main() {
printf("Hello, World!\n"); // 这条指令存储在代码区
return 0;
}
b.静态/全局数据区
① 初始化数据区:
存储内容:已初始化的全局变量和静态变量(包括全局常量)。
特点:程序启动时分配,程序结束时释放。生命周期贯穿整个程序运行期。
② 未初始化数据区:
存储内容:未初始化的全局变量和静态变量(默认初始化为
0或NULL)。特点:程序启动时由系统自动清零。大小在编译时确定。
cpp
int global_var = 10; // 初始化全局变量
static int static_var = 20; // 初始化静态变量
const int const_var = 30; // 全局常量(可能存储在只读数据区)
int uninit_global; // 未初始化全局变量(存储在 BSS 段)
static int uninit_static; // 未初始化静态变量(存储在 BSS 段)
c.栈区
存储内容:局部变量(非静态)、函数参数和返回值、函数调用时的上下文(如返回地址、寄存器值)。
特点:自动管理、高效但有限、后进先出(
LIFO)
cpp
void foo() {
int local_var = 5; // 局部变量存储在栈区
char buffer[1024]; // 栈空间可能不足导致溢出
}
d.堆区
存储内容:动态分配的内存(通过
malloc、calloc、realloc或new分配)。特点:手动管理、大小灵活、访问速度较慢。
cpp
int *arr = malloc(10 * sizeof(int)); // 动态分配数组
free(arr); // 必须手动释放
e.命令行参数和环境变量区
存储内容:程序启动时的命令行参数(
argc和argv),环境变量(如PATH、HOME等)。特点:由操作系统在程序启动时设置、通常通过
main函数的参数访问
cpp
int main(int argc, char *argv[]) {
// argv[0] 是程序名,argv[1...] 是参数
}
内存示意图

注意事项
① 栈溢出 : 递归过深或局部变量过大可能导致栈溢出(如
buffer[1000000])。② 内存泄漏: 忘记释放堆内存(如
malloc后未free)。**③ 野指针:**访问已释放的堆内存或未初始化的指针。
④ 段错误: 试图修改代码区或只读数据区(如修改字符串常量
char *s = "abc"; s[0] = 'x';)。
3.参考
**考察范围:**C语言内存体系的理解。
回答思路: 从 代码区 + 静态区 + 堆区 + 栈区 来进行回答。
答案:
代码区(Text Segment):存放程序指令(如函数代码),只读且共享。
静态区(Data Segment):包含初始化的全局/静态变量、未初始化的全局/静态变量(BSS),生命周期贯穿整个程序。
堆区(Heap):动态分配内存的区域(如
malloc/free),由程序员手动管理,空间最大但分配效率较低。栈区(Stack):由编译器自动管理,存放函数调用信息、局部变量和参数,分配高效但空间有限。
希望这些内容对大家有所帮助!
感谢大家的三连支持!