zoukankan      html  css  js  c++  java
  • C语言中的内存管理

    开始陆续的发一下唐老师视频的笔记吧,顺便带一些正冲哥书的的内容。不能一下都发出来,因为内容发多了自己也受不了,而且发的都是学习视频时候的一些笔记,可能会有一些问题不是很清晰。

    先说一下C语言中的内存管理。

    1.动态内存分配

    ①原因:程序运行过程中,很有可能需要一些额外的内存空间。
    ②动态内存从哪里来,还给谁?
      这块是内存是系统专门预留出来的,给程序动态的分配和动态的归还的。
      当free函数的参数为空的时候,那么我们的free什么事都不做了。
      clloc和realloc的用法见截图:

    动态内存是从堆上分配空间的,具体形式以int为例 int *a = (int*)malloc (5*(sizeof(int)))。

    2.栈,堆,静态存储区
    ①没有栈就没有函数,就没有局部变量。函数是依赖于栈的存在而存在的。
      这个栈不是数据结构中的栈,是内存栈。
      后进先出的原则没有改变。
      每一个函数的调用在内存中都会产生活动记录,这个活动记录包括函数的参数和返回地址等等,所以可以说这个活动记录就是在栈上面建立的。


      按照老唐的话说,一个栈应该包含的有如下数据
      (1)返回地址(子函数ebp在返回ebp的时候应该指向的位置)
      (2)old ebp
      (3)数据(包含函数中的所有数据)(这也是子函数和子函数esp指向的位置)
      栈的增长方式:从栈底一直向上增长。


    ②因为栈的数据在函数执行结束之后就会被释放了,没有传递到函数的外部,所以产生了程序中的堆。
    堆中申请的内存在程序主动释放前一直有效。堆空间就是为了动态内存分配而产生的。
    堆空间必须要通过申请才可以得到。
    堆管理的方法有很多。老唐讲的链表法很清晰。
    ③程序静态存储区
    程序静态存储区在程序运行的时候就已经开辟出来了,同时在程序运行结束的时候释放。
    静态程序区的大小在程序编译的时候就已经规定好了。
    程序的静态存储区主要用于保存程序运行的全局变量和静态变量。

    这三个存储区是程序中所用的绝大数区域。

    3.程序的内存布局。
    程序中除了栈,堆,静态存储区以外还有其他的存储区么?
    可执行程序的布局:
    (1)可执行程序的文件头:操作系统来读这个可执行程序的文件头,看一下这个文件头里的信息是不是系统想要的信息。
    (2).text段,这里面保存的是主函数和子函数的运行内容。
    (3).data段,这里面保存的是申请并且赋值的全局变量,以及申请并且赋值的静态变量。
    (4).bss段,这里面保存的是申请但是没有赋值的全局变量,以及申请并没有赋值的静态变量。
    通过上面的四个段,没有找到保存申请并且赋值的局部变量的地方也没有申请但是没有赋值的局部变量的地方。因为局部变量是保存在栈空间中的。

     程序文件的布局是程序可执行文件的布局,并不是程序执行过程中的布局,堆栈和静态存储区才是程序执行过程(进程)的布局。
    text段、data段和bss段在程序运行的时候(进程)会被映射到进程空间中。

     栈和堆是要等到程序运行之后由操作系统进行分配的。

    这里的栈可以理解成为一个弹性的东西,所以在程序运行的时候映射过来的段和随着栈的行为来行动。
     静态存储区在程序编译的时候就已经开始分配空间了,所以静态存储区在程序运行的时候依然可以对应一部分程序的可执行文件的空间。函数的地址对应的是程序text段中的某个地址。

    4.野指针通常是因为指针变量保存的值不是一个合法内存地址造成的。
      在C中没有任何手段来判别一个指针是否是野指针。

    ①局部指针变量没有被初始化。

    #include <stdio.h>
    
    	struct student
    	{
    		char *name ;
    		int no ;
    	};
    	int main()
    	{
    		struct student s; 
    		//这个strcpy函数是把后面指针变量的内存内容赋给前面指针变量的内容。但是前面的指针变量指向的内存并不确定,所以造成了野指针。
    		strcpy(s.name,"haha");
    		s.no = 10;
    		printf ("%s,%d",s.name,s.no);
    	}

    解决办法:

    #include <stdio.h>
    	#include <string.h>
    	struct student
    	{
    		char *name  ;
    		int no ;
    	};
    	int main()
    	{
    		struct student s; 
    		s.name = (char*)malloc(sizeof(char)*20);
    		strcpy(s.name,"haha");
    		s.no = 10;
    		printf ("%s,%d",s.name,s.no);
    	}

    上面程序少了一个free(s.name).

    ②指针释放后程序就归还了这片空间,既然归还了那么就不可以再使用这篇空间了。
         使用已经释放后的指针。
      例程:

    #include <stdio.h>
    	#include <string.h>
    	#include <stdlib.h>
    
    	int f(char*i)
    	{
    		printf ("%s
    ",i);
    		free (i);
    	}
    	int main()
    	{
    		char *p = (char*)malloc(sizeof(char)*5);
    
    		strcpy(p, "qwertyui");
    
    		f(p);
    		printf ("%s
    ",p);
    	}

    解决办法:

     #include <stdio.h>
    	#include <string.h>
    	#include <stdlib.h>
    
    	int f(char*i)
    	{
    		printf ("%s
    ",i);
    		//free (i);
    		return 0;
    	}
    
    	int main()
    	{
    		char *p = (char*)malloc(sizeof(char)*10);
    
    		strcpy(p, "qwertyui");
    
    		f(p);
    		
    		printf ("%s
    ",p);
    
    		free (p);
    
    		return 0;
    	}

    ③指针所指向的变量在指针之前被销毁。

    5.内存操作中的经典错误
    ①非法内存操作
    ②内存分配成功但是并没有初始化

    例程;

    #include <stdio.h>
    	int main()
    	{
    		char *p = (char *)malloc(10);
    		printf(p);
    		free (p);
    		return 0;
    	}

    ③数组越界
    ④内存泄露
    ⑤多次释放指针----谁申请谁释放。
    ⑥使用已经释放的内存。

    6.老唐自己的交通规则
    ①使用malloc之后应该立刻判断返回值是否为空。可以杜绝操作0地址里面的内容,这个地址是为操作系统使用的。

    例程:

    #include<stdio.h>
    	int main()
    	{
    		int *p = (int*)malloc(sizeof(int)*5);
    		
    		if (p !== NULL)
    		{
    			.....
    		}
    		free (p);
    		return 0;
    	}

    ②牢记数组长度,防止越界操作。----使用柔性数组。
    ③动态申请的操作必须和释放操作相匹配。
    ④free指针以后,被free的指针要赋值为空。

  • 相关阅读:
    java8
    java8
    java8
    java8
    java8
    java8
    java8
    java8
    GUC-13 生产者和消费者案例-旧
    GUC-14 ForkJoin
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3235293.html
Copyright © 2011-2022 走看看