zoukankan      html  css  js  c++  java
  • 《C语言笔记:三种内存来源》

    一,三种内存来源(程序可以操控的内存空间)

      1,在一个C程序中,能够获取的内存就是三种情况:栈(stack)、堆(heap)、数据区(.data)。

    二,栈的详解

      1,栈的内存空间,程序运行时自动分配&自动回收,栈是自动管理的,程序员不需要手工干预。

      2,栈内存在程序中就是那一块大小有限的内存空间,程序反复使用这一块空间。不断的在这一块空间中自动分配&“自动回收”。

      3,由于栈内存反复使用,并且每次使用后程序不会去清理之间使用的空间,因此分配到时保留原来的值,是一块脏内存。所以在定义一个分布在栈上的局部变量时,一定要先初始化,不然其值就是随机的。

      4,函数不能返回一个栈变量的指针,因为这个空间时临时的。

      5,栈会溢出,因为操作系统事先给定了栈的大小,如果在函数中无穷尽的分配栈内存总能用完。

    三,堆内存的详解

      1,操作系统堆管理器管理堆内存,堆管理器是操作系统的一个模块,堆管理内存分配灵活,按需分配。

      2,程序手动申请和释放,需要程序员用malloc申请,free释放。

      3,堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏的。

      4,堆内存只在malloc和free之间属于一个进程,可以访问,在malloc之前和free之后都不能访问,否则会出现不可与预料的错误。

    四,堆内存使用    void *malloc(size_t size);  void free(void *ptr);

      1,malloc返回的是一个void * 类型的指针,实质上malloc返回的时堆管理器分配的本次申请的那段内存空间的首地址(返回的值其实是一个数字,这个数字表示一个内存地址)。

      2,为什么要使用void *类型呢?

        主要原因是:malloc帮我们分配内存时只是分配了内存空间,至于这段空间被用来存储什么类型的元素malloc是不关心的,由程序自己决定。

      3,什么是void *类型?

        void 类型不表示没有类型,而表示万能类型。void的意思是这个数据的类型当前是不确定的,在需要的时候可以再去指定它的具体类型。void *类型是一个指针类型,这个指针本身占四个字节,但是指针指向的类型是不确定的,换句话说,这个指针在需要的时候可以被强制转化成任何一种确定类型的指针,也就是说这个指针可以指向任何类型的变量。

      4,malloc的返回值:成功申请空间后返回这个内存空间的指针,申请失败时返回NULL。所以malloc获取的内存使用前一定要先检验是否为NULL。

      5,malloc申请的内存用完后要free释放。free会告诉堆管理器这段内存我用完了你可以回收了,堆管理器回收了这段内存后这段内存当前进程就不应该再使用了。因为释放后堆管理器就可能把这段内存分配给其他进程。

      6,在调用free释放这段内存前,指向这段内存的指针一定不能丢(也就是不能给p赋其他值)。因为一旦p丢失,这段malloc来的内存就永远的丢失了,直到当前程序结束时,操作系统才能回收这段内存。

    五,malloc的一些细节

      1,malloc(0);返回的时NULL还是一个有效的指针?

        实际分配了32字节的一段内存,并且返回了这段内存的地址。这个答案是不确定的,因为C语言并没有明确规定malloc(0)的具体表现,由各个平台malloc函数库的实现者来定义。

      2,malloc(4);gcc中malloc默认最小是以32字节为单位分配的。如果malloc小于32B大小时,会返回一个32字节大小的内存。malloc实现时没有实现任意自己的分配,而是允许一些大小的块内存的分配。也就是以某个字节的块内存为单位分配。

      64位平台

    #include<stdio.h>
    #include<stdlib.h>
    
    
    #pragma pack(4)
    int main(void)
    {
        #if 0
        char *p = malloc(0);
        char *a = malloc(1);
        if(NULL == p)
        {
            printf(" malloc error 
    ");
        }
    
        printf("  p = %p 
    ",p);       //p = 0xc73010
        printf("  a = %p 
    ",a);       //a = 0xc73030
    
        printf("  p size = %d 
    ",a-p);    //32
        #else
        char *p = malloc(4);
        char *a = malloc(1);
        if(NULL == p)
        {
            printf(" malloc error 
    ");
        }
    
        printf("  p = %p 
    ",p);       //p = 0x2497010
        printf("  a = %p 
    ",a);       //a = 0x2497030
    
        printf("  p size = %d 
    ",a-p);  //32
        #endif
    
    
    }
    
    #pragma pack()

    六,代码段、数据段、bss段

      1,编译器在编译程序的时候,将程序中所有的元素分成了一些组成部分,各部分构成一个段,所以说段是可执行程序的组成部分。

      2,代码段:代码段就是程序中的可执行部分,就是函数堆叠组成的,里面是一个个的函数定义。char *p = "linux;定义字符串时,字符串”linux“实际上被分配在代码段。const型常量,有时也是被放在代码段。

      3,数据段(也被称为数据区、静态数据区、静态区):数据段就是程序中的数据。显示初始化为非零的 全局变量和static修饰的静态局部变量放在数据段。

      4,.bss段:未初始化或显示初始化为零的 全局变量和静态局部变量放在.bss段。

    七,const常量

      1,C语言种使用const关键字来定义常量,常量就是不能被改变的量。

      2,const的实方法至少有两种:

        第一种,就是编译时将const修饰的变量放在代码段,以实现不能修改(常见于各种单片机的编译器)。

        第二种,就是由编译器来检查以确保const型的常量不能被修改,实际上const型常量还是和普通变量一样放在数据段中(gcc中就是这样实现的),这个时候const 常量实际上是可以被修改掉的,只要骗过编译器的类型检查。

    八,总结

      1,相同点:三种获取内存的方法,都可以给程序提供内存,都可以用来定义变量给程序用。

      2,不同点:栈内存对应的是C种普通的局部变量(别的变量还用不了栈,而且栈是自动的,由编译器和运行时的环境共同来提供服务,程序员无法手工控制),堆内存完全是独立于程序存在和管理的(通过API由堆管理器管理),程序需要内存时可以手工申请malloc,使用完成后要free;数据段对于程序来说对应程序中的全局变量和静态局部变量。

  • 相关阅读:
    async简单使用
    node调用phantomjs-node爬取复杂页面
    mongodb3 ubuntu离线安装(非apt-get)及用户管理
    2040-亲和数(java)
    JavaScript闭包简单理解
    nodejs构建多房间简易聊天室
    linux下安装nodejs及npm
    EventBus轻松使用
    mysql用户创建及授权
    python中json的基本使用
  • 原文地址:https://www.cnblogs.com/xuxianshen/p/13944944.html
Copyright © 2011-2022 走看看