八、内存管理:变量、函数的作用域及生命周期;C语言内存四区模型;堆区内存分配和释放:malloc()、free();内存操作函数:memset()、memcpy()、memmove()、memcmp()

局部变量、静态(static)局部变量、全局变量、静态(static)全局变量、extern全局变量声明、全局函数和静态函数;作用域及生命周期对比 ======================================================================================================== 局部变量 局部变量也叫auto自动变量(auto可写可不写),一般情况下代码块{}内部定义的变量都是自动变量,它有如下特点: 在一个函数内定义,只在函数范围内有效 在复合语句中定义,只在复合语句中有效 随着函数调用的结束或复合语句的结束局部变量的声明声明周期也结束 如果没有赋初值,内容为随机 ------------------------------------------------------------------------------------------- 静态(static)局部变量 static局部变量的作用域也是在定义的函数内有效 static局部变量的生命周期和程序运行周期一样,同时staitc局部变量的值只初始化一次,但可以赋值多次 static局部变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值0,字符型变量赋空字符 ------------------------------------------------------------------------------------------- 全局变量 在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明 全局变量的生命周期和程序运行周期一样 不同文件的全局变量不可重名 全局变量的作用域:默认只在文件内部,若跨文件的变量,可以通过声明的方式将作用域导出(就是在其他文件中显式地声明变量) ------------------------------------------------------------------------------------------- 静态(static)全局变量 在函数外定义,作用范围被限制在所定义的文件中 不同文件静态全局变量可以重名,但作用域不冲突 static全局变量的生命周期和程序运行周期一样,同时staitc全局变量的值只初始化一次 ------------------------------------------------------------------------------------------- extern全局变量声明 extern int a;声明一个变量,这个全局变量在别的文件中已经定义了,这里只是声明,而不是定义。 ------------------------------------------------------------------------------------------- 全局函数和静态函数 在C语言中函数默认都是全局的,使用关键字static可以将函数声明为静态,函数定义为static就意味着这个函数只能在定义这个函数的文件中使用,在其他文件中不能调用,即使在其他文件中声明这个函数都没用。 对于不同文件中的staitc函数名字可以相同。 注意: 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰。 同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用。 所有的函数默认都是全局的,意味着所有的函数都不能重名,但如果是staitc函数,那么作用域是文件级的,所以不同的文件static函数名是可以相同的。 ------------------------------------------------------------------------------------------- 局部变量: 概念:定义在函数 内 部的变量。 作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。 生命周期:从变量定义开始,函数调用完成。--- 函数内部。 全局变量: 概念:定义在函数 外 部的变量。 作用域:从定义位置开始,默认到本文件内部。 其他文件如果想使用,可以通过声明方式将作用域导出。 生命周期:程序启动开始,程序终止结束。 --- 程序执行期间。 static全局变量: 定义语法: 在全局变量定义之前添加 static 关键字。 static int a = 10; 作用域:被限制在本文件内部,不允许通过声明导出到其他文件。 生命周期:程序启动开始,程序终止结束。 --- 程序执行期间。 static局部变量: 定义语法: 在局部变量定义之前添加 static 关键字。 特性: 静态局部变量只定义一次。在全局位置。 通常用来做计数器。 作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。 生命周期:程序启动开始,程序终止结束。 --- 程序执行期间。 全局函数: 函数 定义语法: 函数原型 + 函数体 生命周期:程序启动开始,程序终止结束。 --- 程序执行期间。 static函数: 定义语法:static + 函数原型 + 函数体 static 函数 只能在 本文件内部使用。 其他文件即使声明也无效。 生命周期:程序启动开始,程序终止结束。 --- 程序执行期间。

C语言内存四区模型:代码区(text segment)、未初始化数据区(BSS)、全局初始化数据区/静态数据区(data segment)、栈区(stack)、堆区(heap) ============================================================================================ C语言内存四区模型 https://blog.csdn.net/qq_39112646/article/details/102651953 程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区。 代码区(text segment) 加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。 存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。 未初始化数据区(BSS) 加载的是可执行文件BSS段,位置可以分开亦可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。 存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空(NULL)。 全局初始化数据区/静态数据区(data segment) 加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。 该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。 栈区(stack) 栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。 堆区(heap) 堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

堆区内存分配和释放:malloc()、free() ======================================================================================================== malloc() #include <stdlib.h> void *malloc(size_t size); 功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。分配的内存空间内容不确定,一般使用memset初始化。 参数: size:需要分配内存大小(单位:字节) 返回值: 成功:分配空间的起始地址 失败:NULL --------------------------------------------------------------------------------------- free() #include <stdlib.h> void free(void *ptr); 功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址。对同一内存空间多次释放会出错。 参数: ptr:需要释放空间的首地址,被释放区应是由malloc函数所分配的区域。 返回值:无

内存操作函数:memset()、memcpy()、memmove()、memcmp() ======================================================================================================== memset() #include <string.h> void *memset(void *s, int c, size_t n); 功能:将s的内存区域的前n个字节以参数c填入 参数: s:需要操作内存s的首地址 c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255 n:指定需要设置的大小 返回值:s的首地址 --------------------------------------------------------------------------------------- memcpy() #include <string.h> void *memcpy(void *dest, const void *src, size_t n); 功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。 参数: dest:目的内存首地址 src:源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错 n:需要拷贝的字节数 返回值:dest的首地址 --------------------------------------------------------------------------------------- memmove() memmove()功能用法和memcpy()一样,区别在于:dest和src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低些。 --------------------------------------------------------------------------------------- memcmp() #include <string.h> int memcmp(const void *s1, const void *s2, size_t n); 功能:比较s1和s2所指向内存区域的前n个字节 参数: s1:内存首地址1 s2:内存首地址2 n:需比较的前n个字节 返回值: 相等:=0 大于:>0 小于:<0

malloc()、free()/memset()、memcpy()、memmove()、memcmp() 使用示例 ======================================================================================================== // int* p = (int*)malloc(sizeof(int)*10);//申请堆空间,返回指针 // memset(p, 1, 20); //memset()是做内存初始化工作:将指针P的内存区域的前20个字节以参数‘1’填入;注意:memset()是按字节重置的! // memcpy(p, src, 12); //拷贝src所指的内存内容的前12个字节到p所值的内存地址上。 // memmove()功能用法和memcpy()一样,区别在于:dest和src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低些。 // printf("%d ",memcmp(arr1, arr2, 12));//0;比较s1和s2所指向内存区域的前n个字节,相等:=0;大于: > 0;小于: < 0 // free(p); //释放堆空间 //堆空间使用 int main() { //申请堆空间,返回指针 int* p = (int*)malloc(sizeof(int)*6); if (p == NULL) { printf("malloc error!\n"); return -1; } for (size_t i = 0; i < 6; i++) { //返回了堆指针后,需要进行数据初始化,否则为乱码 printf("%d ", *(p + i)); //-842150451 -842150451 -842150451 -842150451 -842150451 -842150451 } memset(p, 1, sizeof(int) * 3); //***将指针P的内存区域的前20个字节以参数‘1’填入 for (size_t i = 0; i < 6; i++) { printf("%d ", *(p + i)); //16843009 16843009 16843009 -842150451 -842150451 -842150451 //16843009 = 1+256+65536+16777216 } memset(p, 0, sizeof(int) * 6); //***上面仅为了测试,实际应该置0 for (size_t i = 0; i < 6; i++) { printf("%d ", *(p + i)); //0 0 0 0 0 0 } int src[] = {1,2,3,4,5}; memcpy(p, src, sizeof(int) * 3); //***拷贝src所指的内存内容的前12个字节到p所值的内存地址上。 for (size_t i = 0; i < 6; i++) { printf("%d ", *(p + i)); //1 2 3 0 0 0 } for (size_t i = 0; i < 6; i++) { p[i] = i + 10; } int arr1[] = { 1,2,3,4,5,6 }; int arr2[] = { 1,2,3,0 }; printf("%d ",memcmp(arr1, arr2, 12));//0;比较s1和s2所指向内存区域的前n个字节,相等:=0;大于: > 0;小于: < 0 printf("%d ",memcmp(arr1, arr2, 16));//1 //释放堆空间 free(p); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!