美文网首页
C高阶4:内存

C高阶4:内存

作者: jdzhangxin | 来源:发表于2019-04-14 13:42 被阅读0次

    1. 结构体字节对齐

    • 试一试
    struct S1{
        char c1;
        char c2;
        int n;
    };
    struct S2{
        char c1;
        int n;
        char c2;
    };
    
    printf("sizeof(struct S1) = %ld\n",sizeof(struct S1));
    printf("sizeof(struct S2) = %ld\n",sizeof(struct S2));
    

    在C语言里,结构体所占的内存是连续的,但是各个成员之间的地址不一定是连续的。所以就出现了"字节对齐"。

    • 字节对齐默认原则

      1. 结构体变量的大小,一定是其最大的数据类型的大小的整数倍,如果某个数据类型大小不够,就填充字节。
      2. 结构体变量的地址,一定和其第一个成员的地址是相同的。
    • 练习

    struct Box{
        int height;
        char a[10];
        double width; 
        char type;
    };
    
    int main(void) {
        struct Box box;
        printf("box = %p\n", &box);
        printf("box.height = %p\n", &box.height);
        printf("box.a = %p\n", box.a);
        printf("box.width = %p\n", &box.width);
        printf("box.type = %p\n", &box.type);
        printf("box = %d\n", sizeof(box));
        return 0;
    }
    

    2. 内存四区

    执行下面代码,分析代码中各种变量的地址特点。

    #include <stdio.h>
    #include <stdlib.h>
    
    void f1(){
      int f1local1;
      int f1local2;
      static int f1static1;
      static int f1static2;
      int* f1dynamic1 = malloc(sizeof(int));
      int* f1dynamic2 = malloc(sizeof(int));
      printf("--------------f1()----------------\n");
      printf("&f1local1   = %p\n",&f1local1);
      printf("&f1local2   = %p\n",&f1local2);
      printf("&f1static1  = %p\n",&f1static1);
      printf("&f1static2  = %p\n",&f1static2);
      printf("f1dynamic1  = %p\n",f1dynamic1);
      printf("f1dynamic2  = %p\n",f1dynamic2);
      printf("\"f1string1\" = %p\n","f1string1");
      printf("\"f1string2\" = %p\n","f1string2");
      printf("--------------f1()----------------\n");
      free(f1dynamic1);
      free(f1dynamic2);
    }
    void f2(){
      int f2local1;
      int f2local2;
      static int f2static1;
      static int f2static2;
      int* f2dynamic1 = malloc(sizeof(int));
      int* f2dynamic2 = malloc(sizeof(int));
      printf("--------------f2()----------------\n");
      printf("&f2local1   = %p\n",&f2local1);
      printf("&f2local2   = %p\n",&f2local2);
      printf("&f2static1  = %p\n",&f2static1);
      printf("&f2static2  = %p\n",&f2static2);
      printf("f2dynamic1  = %p\n",f2dynamic1);
      printf("f2dynamic2  = %p\n",f2dynamic2);
      printf("\"f2string1\" = %p\n","f2string1");
      printf("\"f2string2\" = %p\n","f2string2");
      printf("--------------f2()----------------\n");
      free(f2dynamic1);
      free(f2dynamic2);
    }
    
    int static1;
    int static2;
    int main(){
      int local1;
      int local2;
      int* dynamic1 = malloc(sizeof(int));
      int* dynamic2 = malloc(sizeof(int));
      printf("&local1   = %p\n",&local1);
      printf("&local2   = %p\n",&local2);
      printf("&static1  = %p\n",&static1);
      printf("&static2  = %p\n",&static2);
      printf("dynamic1  = %p\n",dynamic1);
      printf("dynamic2  = %p\n",dynamic2);
      printf("\"string1\" = %p\n","string1");
      printf("\"string2\" = %p\n","string2");
      printf("&f1 = %p\n",&f1);
      printf("&f2 = %p\n",&f2);
      f1();
      f2();
    }
    

    分析执行结果

    &local1   = 0x7ffc8b8120fc
    &local2   = 0x7ffc8b8120f8
    &static1  = 0x601060
    &static2  = 0x601064
    dynamic1  = 0x1d10010
    dynamic2  = 0x1d10030
    "string1" = 0x400b8f
    "string2" = 0x400ba7
    &f1 = 0x40060d
    &f2 = 0x400707
    --------------f1()----------------
    &f1local1   = 0x7ffc8b8120cc
    &f1local2   = 0x7ffc8b8120c8
    &f1static1  = 0x601050
    &f1static2  = 0x601054
    f1dynamic1  = 0x1d10050
    f1dynamic2  = 0x1d10070
    "f1string1" = 0x400a2f
    "f1string2" = 0x400a4b
    --------------f1()----------------
    --------------f2()----------------
    &f2local1   = 0x7ffc8b8120cc
    &f2local2   = 0x7ffc8b8120c8
    &f2static1  = 0x601058
    &f2static2  = 0x60105c
    f2dynamic1  = 0x1d10070
    f2dynamic2  = 0x1d10050
    "f2string1" = 0x400af7
    "f2string2" = 0x400b13
    --------------f2()----------------
    

    上面结果每次执行结果会有所变化,不同计算机结果也会不同。但是会有如下规律:

    1. 局部变量、静态变量、动态分配内存、字符串常量与函数分别放在一起,即使在不同的函数中。
    2. 变量的存放地址大小有如下特点:

      字符串常量与代码 < 静态变量 < 动态分配内存 < 局部变量

    3. 静态变量、动态分配内存、字符串常量与函数的相邻变量地址是递增的。局部变量相邻变量地址是递减的。
    4. 字符串常量与函数是在一起的。

    上面说明代码在内存中是按类型存放在不同的区里面的。

    gcc -S test.c
    gcc --save-temp test.c
    readelf -S ./a.out
    

    2.1 栈区(stack)

    由编译器自动分配和释放,主要是存放函数参数的值,局部变量的值。

    2.2 堆区(heap)

    由程序员自己申请分配和释放,需要malloc()calloc()realloc()函数来申请,用free()函数来释放如果不释放,可能出现指针悬空/野指针。

    函数不能返回指向栈区的指针,但是可以返回指向堆区的指针。

    2.3 数据区(data)

    变量标有static关键字,保存了静态变量。
    1. 初始化的全局变量和初始化的静态变量,在一块区域;
    2. 未初始化的全局变量和未初始化的静态变量,在一块区域,称作BSS(Block Started by Symbol:以符号开始的块);
    3. 静态变量的生命周期是整个源程序,而且只能被初始化一次,之后的初始化会被忽略。
    (如果不初始化,数值数据将被默认初始化为0, 字符型数据默认初始化为NULL)。

    整个数据区的数组,在程序结束后由系统统一销毁。

    2.4 代码区(code)

    用于存放编译后的可执行代码,二进制码,机器码。

    3. 堆和栈的区别

    No. 比较方面
    1 管理方式 由系统自动管理,以执行函数为单位 由程序员手动控制
    2 空间大小 空间大小编译时确定(参数+局部变量) 具有全局性,总体无大小限制。
    3 分配方式 函数执行,系统自动分配;函数结束,系统立即自动回收。 使用new/malloc()手动分配释放;使用delete/free()手动释放
    4 优点 使用方便,不需要关心内存申请释放。 可以跨函数使用。
    5 缺点 只能在函数内部使用。 容易造成内存泄露。

    4. 显示目标文件区段大小:size命令

    • 各区段含义

      No. 区段 名称 含义
      1 text 代码段(code segment/text segment) 存放程序执行代码的内存区域。该区域的大小在运行前已确定,且通常属于只读。可能包含一些只读的常数变量,例如字符串常量等。
      2 data 数据段(data segment) 存放程序中已初始化的全局变量的内存区域。数据段属于静态内存分配。
      3 bss BSS段(bss segment) 存放程序中未初始化的全局变量的内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

      dechex是前面三个区域的和,dec是十进制,hex是十六进制。

    • 没有显示的区段

      No. 区段 含义
      1 栈(stack) 存放程序临时创建的局部变量,也就是函数括弧{}中定义的变量(不包括static声明的变量)。在函数被调用时,参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
      2 堆(heap) 存放程序动态分配的内存段,大小并不固定,可动态扩张或缩减。当调用malloc()等函数分配内存时,堆被扩张;当调用free()等函数释放内存时,堆被缩减。

    相关文章

      网友评论

          本文标题:C高阶4:内存

          本文链接:https://www.haomeiwen.com/subject/rwucsqtx.html