重庆万州网站建设报价,网站流量突然增加,免费查询企业,网站在百度的图标显示不正常更多精彩内容..... #x1f389;❤️播主の主页✨#x1f618; Stark、-CSDN博客 本文所在专栏#xff1a; 学习专栏C语言_Stark、的博客-CSDN博客 其它专栏#xff1a; 数据结构与算法_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客 座右铭❤️播主の主页✨ Stark、-CSDN博客 本文所在专栏 学习专栏C语言_Stark、的博客-CSDN博客 其它专栏 数据结构与算法_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客 座右铭梦想是一盏明灯照亮我们前行的路无论风雨多大我们都要坚持不懈。 在前面我们学习C语言的时候我们就提到过内存的概念介绍了内存的分区以及各个区域负责的事情并且给出了一些内存函数使用。
C语言的内存知识_c语言内存-CSDN博客
本节我们主要是学习内存的管理。 一、内存分区模型
一般我们将内存划为四个或五个区而真实的内存远远不止这四五个区。我们先来回顾一下内存分区的知识 Ⅰ.栈区(stack)由编译器自动分配释放。存放函数的参数值局部变量的值等。 Ⅱ.堆区(heap)一般由程序员分配释放(动态内存申请和释放)。若程序员不释放程序结束时可能由操作系统回收。 Ⅲ.全局区(global)/静态区(static)全局变量和静态变量是放在一块的初始化的全局变量和静态变量在一块儿区域未初始化的全局变量和未初始化的静态变量在相邻的另一块儿区域该区域在程序结束后由操作系统释放。 Ⅳ.常量区(const)字符串常量和其它常量的存储位置程序结束后操作系统回收 Ⅴ.程序代码区(code)存放函数体的二进制代码 下面我们给出一段代码进行一下简单的验证
int g_a;//全局变量
int g_b;
static int s_g_a;//静态全局变量
static int s_g_b;
const int c_g_a1;//全局常量
const int c_g_b1;
const static int c_s_g_a;//静态全局常量
const static int c_s_g_b;
int main() {cout 全局变量 endl;cout g_a endl;cout g_b endl;cout 静态全局变量 endl;cout s_g_a endl;cout s_g_b endl;cout 全局常量 endl;cout c_g_a endl;cout c_g_b endl;cout 静态全局常量 endl;cout c_s_g_a endl;cout c_s_g_b endl;int l_a;//局部变量int l_b;cout 局部变量 endl;cout l_a endl;cout l_b endl;const int c_l_a1;//局部常量const int c_l_b1;cout 局部常量 endl;cout c_l_a endl;cout c_l_b endl;static int s_l_a;//静态局部变量static int s_l_b;cout 静态局部变量 endl;cout s_l_a endl;cout s_l_b endl;const static int c_s_l_a;//静态局部常量const static int c_s_l_b;cout 静态局部常量 endl;cout c_s_l_a endl;cout c_s_l_b endl;//字面量(字符串常量)cout 字面量地址 (123) endl;return 0;
} 从中我们不难发现本次运行时全局区的数据全部在00007FF69DC5开头的内存地址处并且静态区的也是放在了这段内存中而且变量在F1段常量在BB段。另外不被const修饰的普通常量(字符串字面量)的地址也存在了00007FF69DC5这段内存中且在C010处。综上分析我们可以知道 大致就是这么一个分布。 Linux系统由操作系统预留空间一般默认8MWindows系统由编译器预留空间其中VC一般默认1M。当我们使用栈区空间时都是比较节俭的所以我们程序员一般会主动向堆区申请空间以免栈溢出。
如果把内存分为四个区的话一般不包括常量区。而其它四区的建立流程是 流程说明操作系统把物理硬盘代码load到内存操作系统把c代码分成四个区操作系统找到main函数入口执行。 二、函数调用模型
C语言的函数-CSDN博客
在上面将函数的时候我们提过函数栈帧的概念。那么这就是函数调用的模型。学好之后对函数的递归学习有很大帮助哦。 1、 一个主程序有n个函数组成C编译器会建立有几个堆区有几个堆区
2、函数嵌套调用时实参地址传给形参后C编译器如何管理变量的生命周期
分析通过函数fa()调用函数fb()通过参数传递的变量内存空间能用嘛 三、内存管理
在前文的内存知识章节我们只说了内存函数没有说内存管理函数。对于C语言内存管理函数包括mallocrealloccalloc以及free四个。包含在头文件stdlib.h中。
1.malloc()函数
用处申请堆区空间
函数原型void* malloc(size_t _Size)
函数解析参数是需要申请的空间字节大小返回值是void*类型的指针方便类型转换为其他类型的指针。
使用例子
int *p(int*)malloc(sizeof(int)*5);//申请能存放五个int类型数据的空间
//相当于一个存在于堆区的数组int [5]但不是。
2.realloc()函数
用处申请的空间不足需要扩容
函数原型void* realloc(void *_Block,size_t _Size)
函数解析void*的用处全都是方便类型转换_Size是字节大小。_Block是需要扩容的指针
使用例子
int * p(int*)malloc(sizeof(int)*4);
for(int i0;i4;i){p[i]i;
}
//此时我想对p[4]进行赋值明显已经越界了那我就扩容
p (int*)realloc(p,sizeof(int)*8);
p[4]15;
3.calloc()函数
用处开辟空间的同时赋予一定数量的内存一个初始默认值
函数原型void* calloc(size_t _Count,size_t _Size)
函数解析类似于malloc不过参数多了一个_Count,这个参数的意思是在申请空间的同时为前Count块内存赋予初始默认值。
使用例子
int n;cinn;//准备开辟多少 个 空间。
int *p(int*)calloc(n,sizeof(int)*n);//个数*每个所占字节总字节
for(int i0;in;i){coutp[i] ;
}
4.free()函数
用处释放申请的堆区空间
函数原型void free(void* _Block)
函数解释将_Block指向的堆区空间进行释放。
使用例子
free(p);//p为上面申请的指针的任意一个 初识new与delete
C中的new运算符和delete运算符用于在堆上动态分配和释放内存。堆上的内存由程序员手动管理可以在程序的任意位置进行分配和释放。
new运算符的语法如下
new type;
new type[size];其中type可以是任意的数据类型size是一个整数表示要分配的数组大小。
new运算符会在堆上分配一块内存并返回一个指向该内存的指针。如果分配成功new运算符将返回一个指向type的指针如果分配失败new运算符将抛出std::bad_alloc异常。
例如下面的代码分配了一个int数组
int* arr new int[10];delete运算符的语法如下
delete ptr;
delete[] ptr;其中ptr是由new运算符返回的指针。
delete运算符会释放ptr指向的内存并调用析构函数来销毁对象如果有的话。如果ptr为nullptr则delete运算符不会进行任何操作。
如果ptr是由new[]运算符返回的指针则必须使用delete[]运算符来释放内存。如果使用delete运算符来释放由new[]分配的内存行为是未定义的。
例如下面的代码释放了前面分配的int数组
delete[] arr;需要注意的是使用new运算符分配的内存必须使用delete运算符来释放否则就会出现内存泄漏。同样地使用new[]运算符分配的数组必须使用delete[]运算符来释放。
另外还可以使用placement new运算符在已经分配的内存上构造对象以及使用delete运算符销毁对象并释放内存。但这种用法相对较少见一般只在特定场景下使用。
下面是一个完整的示例展示了如何使用new与delete运算符
#include iostream
class MyClass {
public: MyClass(int val) : value(val) { std::cout Constructor called: value std::endl; }~MyClass() {std::cout Destructor called: value std::endl; }
private: int value;
};
int main() { // 动态分配单个对象 MyClass* obj new MyClass(10); // 动态分配数组 MyClass* arr new MyClass[3] { MyClass(1), MyClass(2), MyClass(3) }; // 释放单个对象 delete obj; // 释放数组 delete[] arr; return 0;
}
在这个示例中我们定义了一个简单的类MyClass并展示了如何使用new运算符分配内存和delete运算符释放内存。
剖析new与delete
本段内容为转载深入理解C new/delete, new []/delete[]动态内存管理 - tp_16b - 博客园
在C语言中我们写程序时总是会有动态开辟内存的需求每到这个时候我们就会想到用malloc/free 去从堆里面动态申请出来一段内存给我们用。但对这一块申请出来的内存往往还需要我们对它进行稍许的“加工”后即初始化 才能为我们所用虽然C语言为我们提供了calloc来开辟一段初始化好0的一段内存但面对象中各是各样的数据成员初始化它同样束手无策。同时为了保持良好的编程习惯我们也都应该对申请出来的内存作手动进行初始化。
对此这常常让我们感到一丝繁琐于是到了C中就有了new/delete, new []/delete[] 。用它们便可实现动态的内存管理。 在C中把int 、char..等内置类型的变量也看作对象它们也是存在构造函数和析构函数的只是通常对它们系统调用了默认的构造函数来初始化以及默认的析构编译器优化。所以new int、new int(3)看起来和普通的定义好像没什么区别。 但对于自定义类型的对象此种方式在创建对象的同时还会将对象初始化好于是new/delete、new []/delete []方式管理内存相对于malloc/free的方式管理的优势就体现出来了因为它们能保证对象一被创建出来便被初始化出了作用域便被自动清理。 * malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理清理成员。 * 它们都是动态管理内存的入口。 * malloc/free是C/C标准库的函数new/delete是C操作符。 * malloc/free需要手动计算类型大小且返回值w为void*new/delete可自动计算类型的大小返回对应类型的指针。 * malloc/free管理内存失败会返回0new/delete等的方式管理内存失败会抛出异常。 四、常见内存错误
1.内存泄露
概念内存泄漏Memory Leak是指程序在运行过程中未能释放已经分配的内存空间从而导致可用内存减少的现象。随着时间的推移内存泄漏会导致系统性能下降甚至可能导致程序崩溃。
内存泄漏一般发生在以下情况下 动态分配内存但未释放使用如malloc/new在C/C中申请内存后如果没有相应的free/delete 调用便不会释放这块内存。 引用计数错误在使用引用计数管理内存的情况下如果对象之间形成循环引用可能导致对象的内存无法释放。 不适当地存储指针程序在某个数据结构中存储了对动态分配内存的指针但在不再需要该数据结构时没有释放内存。
为防止内存泄漏开发者可以采取以下措施
使用智能指针在C中可自动管理内存。定期检查和分析代码寻找潜在的内存泄漏。使用内存检测工具如Valgrind等识别和报告内存泄漏问题。确保在不再需要对象时适时释放内存。 总结申请的内存空间没有被释放掉。及时发现和修复内存泄漏问题可以提高程序的稳定性和性能。 2.栈溢出
概念栈溢出Stack Overflow是一种运行时错误发生在程序使用的栈空间超过了栈所分配的大小时。栈是用于存储局部变量和函数调用信息的一块内存区域每当一个函数被调用时相关的局部变量和返回地址等信息会被压入栈中当函数执行完毕时这些信息会被弹出栈外。
栈溢出的常见原因包括 深度递归当一个函数递归调用自身且没有适当的终止条件时可能导致栈帧不断增加最终消耗完分配给栈的内存。 过大的局部变量在函数中声明过大的数组或数据结构也可能导致栈空间不够。 无限循环或错误的循环逻辑在某些情况下如果循环体中的函数调用未能适当退出也可能引发栈溢出。
栈溢出会导致程序崩溃通常会触发操作系统的保护机制从而抛出异常或错误信息。为防止栈溢出可以采取以下措施 避免不必要的深度递归使用迭代法替代递归或者优化递归算法以减少栈深度。 合理使用局部变量尽量避免在栈上使用过大的数据结构考虑使用堆分配如动态分配内存。 监测和调试用使用调试工具跟踪栈的使用情况识别潜在的栈溢出风险。 控制递归深度合理设计递归的深度和适当的退出条件。 总结存在栈区的数组开辟太大递归层次太深。有效减少栈溢出的发生确保程序的稳定性和可靠性。 3.越界访问与野指针
越界访问在数组或存储块的边界之外进行读或写操作。可能导致数据损坏或安全漏洞。
野指针访问未初始化的指针会导致不可预测的行为因为它指向未知的内存区域。
4.双重释放与悬挂指针
悬挂指针指向已经释放的内存地址的指针。使用这种指针进行访问会导致未定义行为。悬挂指针通常是在调用delete或free后未将指针置为nullptr导致的。
双重释放指同一块内存被delete或free多次释放这会导致未定义行为甚至程序崩溃。
5.其他情况
其它情况还包括使用new而不配对delete使用malloc而不配对free或者使用new开辟多块空间没有使用delete[]。
不当使用智能指针虽然智能指针如std::unique_ptr和std::shared_ptr可以帮助管理内存但不正确的使用仍然可能导致悬挂指针、循环引用在std::shared_ptr中等问题。 感谢大家。