记录每次学习的过程,总结学习的内容,希望能给到自己和别人帮助。
嵌入式学习-C语言-内存管理
C代码编译过程
从编译到生成可执行文件的过程:
● 预处理
○ 宏定义展开、头文件展开、条件编译,这里并不会检查语法
● 编译
○ 检查语法,将预处理后文件编译生成汇编文件
● 汇编
○ 将汇编文件生成目标文件(二进制文件)
● 链接
○ 将目标文件链接为可执行程序
进程的内存分布
● 程序运行起来(没有结束前)就是一个进程
● 对于一个C语言程序而言,内存空间主要由五个部分组成 代码区(text)、数据区(data)、未初始化数据区(bss),堆(heap) 和 栈(stack) 组成
○ 有些人直接把data和bss合起来叫做静态区或全局区
代码区: 代码文件内容,函数
文字常量区(data): char * p =“hello”;//常量 不可修改的 (文字常量区,不能修改)
初始化数据(data):已经初始化的全局变量 int a=10; //放在函数外表面的(全局变量,整个程序结束才释放)
未初始化的全局变量 int a;
栈区 局部变量(离开代码块{},就自动释放)
堆区 全局变量例如 static(需要用户手动管理的,申请空间,释放空间)(用户不释放,只要程序没有结束,一直存在)
不一样的区,不一样的功能 不同内存决定不一样的生命周期
● 代码区(text segment)
○ 加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。
● 未初始化数据区(BSS)
○ 加载的是可执行文件BSS段,位置可以分开亦可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。
● 全局初始化数据区/静态数据区(data segment)
○ 加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。
● 栈区(stack)
○ 栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
● 堆区(heap)
○ 堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
堆区内存的使用
申请 malloc( ) ->释放free( )
申请一个malloc空间,p指向这个malloc空间,存着malloc空间的地址。所以*p操作的是指针变量锁指向的空间,操作堆区空间。
free( )函数,释放的是malloc空间,因为p是变量不需要手动释放,只需要释放p指向的手动申请的malloc空间。
跟自动分配空间和指针等一样,只不过这个空间是系统分配还是自己申请。
malloc()
#include <stdlib.h>
void *malloc(size_t size);
功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。
分配的内存空间内容不确定。
参数:
size:需要分配内存大小(单位:字节)
返回值:
成功:分配空间的起始地址
失败:NULL
free()
#include <stdlib.h>
void free(void *ptr);
功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址。
对同一内存空间多次释放会出错。
参数:
ptr:需要释放空间的首地址,被释放区应是由malloc函数所分配的区域。
返回值:无
思考:为什么我释放了空间,指针还是能获取到对应的地址
#include <stdio.h>
#include <stdlib.h>
int main() {
int * p = malloc(sizeof(int));
*p = 123;
printf("%p\n", p);
printf("*p=%d\n", *p);
free(p);
*p = 333;
printf("%p\n", p);
printf("*p=%d\n", *p);
//结果:
// 000002437d2f1430
// *p=123
// 000002437d2f1430
// *p=333
//为什么我释放了空间,指针还是能获取到对应的地址
/*
首先,我们先理解一下malloc函数的作用。
在C语言中,malloc函数用于在内存中动态分配一定大小的未初始化空间,并返回这个空间的指针。
当你使用free函数后,这个空间会被释放,意味着你不能再次使用这个空间,否则可能会出现不可预期的行为,比如数据错误、程序崩溃等。
然而,这里的代码有一些错误。
当使用free(p)释放了p指向的空间后,p自身并不一定被置为NULL。
也就是说,p仍然会保持之前指向的地址。但是这个地址现在已经是无效的,因为它的空间已经被释放了。
如果你再次尝试通过这个指针去访问或者修改数据,那么就会出现问题。
这里释放了空间之后,再次尝试通过p去访问或者修改数据,这是不安全的。
虽然看起来可能没有立即出现问题,但这是一种潜在的错误源。
在实际编程中,释放空间后,应该避免再次使用这个空间。
如果想要安全地释放空间并重置指针,你可以这样做:(释放空间的同时,将指针指向也重置成null)
free(p);
p = NULL; // 将指针重置为 NULL
在这个代码中,当调用free(p)释放空间后,你将p置为NULL。
这样,你就明确地告诉了程序这个指针不再指向任何有效的内存空间。
当再次尝试使用这个指针时,程序就会发现它是指向无效的内存空间(也就是NULL),从而避免出现错误。
*/
return 0;
}
申请数组
返回堆区地址
凡心所向,素履以往,生如逆旅,一苇以航。