C语言之内存结构

236 阅读4分钟

作用域

  • 全局变量与局部变量

    • 局部变量

      auto一般可以省略,一般需要先初始值,否则为垃圾值;作用域:创建的函数内;生命周期:从创建到函数结束。

      int main() {
          auto unsigned int age = 10;
          return EXIT_SUCCESS;
      }
      
    • 全局变量

      • 声明且定义全局变量,默认值为0。作用域:整个项目文件中;生命周期:整个程序运行期间。
      unsigned int age;
      int main(){
      	return EXIT_SUCCESS;
      }
      
      • 只声明一个全局变量,不会分配内存空间,定义可写到外部C文件中
      extern unsigned int age1;
      int main(){
      	return EXIT_SUCCESS;
      }
      
  • 静态全局变量与静态局部变量

    • 静态局部变量: 只能被初始化一次

      void func0() {
          static int a = 1;
          a++;
          printf("a = %d\t", a);
          int b = 1;
          b++;
          printf("b = %d\n", b);
      }
      
      
      int main() {
          for (int i = 0; i < 3; ++i) {
              func0();
          }
          return EXIT_SUCCESS;
      }
      

  • 静态全局变量

    只能在当前文件中使用;静态函数也一样,非静态函数的作用域为整个项目中。

    static unsigned int age2;
    

内存布局

  • 内存分区

C代码经过预处理、编对、汇编、链接四步后生成一个可执行程序。在Linux下,程序是一个普通的可执行文件,以下列出一个二进制可执行文件的基本情况。

  • 内存四区模型

    简单来说,C语言的内存可以分为四块。

    • 代码区

      该区主要存放一些程序指令

    • 数据区(静态区和全局区)

      • 初始化/未初始化的数据: 全局变量、静态全局变量、静态局部变量,默认值都为0
      • 字符串常量
      • define定义的的常量
    • 栈区

      在不同的操作系统中分配给每一个程序的栈区空间大小不同,Windows一般是1~8M之间,Linux是1~16M之间,所以至少有1M的空间大小。

      • 局部变量
      • 数组
      • 结构体
      • 指针
      • 枚举
      • 函数形参
      • 常量
    • 堆区

      主要存放一些大的数据,如音频、视频、图像、文本文件等。

内存空间操作库函数

  • malloc

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int main() {
    
        // 在堆内存中申请一片空间,大小为40个byte
        int *p = malloc(sizeof(int) * 10);
        //创建随机数种子
        srand((unsigned int) time(NULL));
        for (int i = 0; i < 5; ++i) {
            // 随机生成小于50的数
            *(p + i) = rand() % 50;
        }
    
        for (int j = 0; j < 5; ++j) {
            printf("%d\n", p[j]);
        }
    
        // 申请的堆内存空间一定要释放
        if (p) {
            free(p);
        }
    
    
        return EXIT_SUCCESS;
    }
    

    注意事项

    • 申请0空间

      // 其实是一个野指针
      char *p = malloc(0);
      // 为野指针赋值会报错
      *p = 100;
      
    • 多次释放空间会报错

      int main() {
          char *p = malloc(10);
          *p = 100;
          if (p) {
              free(p);
          }
          free(p);
          return EXIT_SUCCESS;
      }
      
    • 置为空后再释放空间不会报错

      int main() {
          char *p = malloc(10);
          *p = 100;
          if (p) {
              free(p);
          }
          p = NULL;
          free(p);
          return EXIT_SUCCESS;
      }
      
    • 字符串操作

      int main() {
          char *p = malloc(10);
          // 不要直接这样操作,因为这是改变了p指向的地址
          // 压根就没有用到malloc申请到的内存,需要用到
          // 字符串的拷贝
      //    p = "Hello";
          // 操作多大的堆内存,就需要申请多大的空间
          strcpy(p, "Hello");
          printf("%s\n", p);
      	if (p) {
              free(p);
          }
          return EXIT_SUCCESS;
      }
      
  • memset

    通过malloc申请到的内存空间,里面的值都是垃圾值,可以通过memset函数来进行初始化

    int main() {
        int *p = malloc(sizeof(int) * 4);
        // 将全部值初始化为0
        memset(p, 0, 40);
        for (int i = 0; i < 4; ++i) {
            printf("%d\n", p[i]);
        }
        if (p) {
            free(p);
        }
        return EXIT_SUCCESS;
    }
    

    假如把初始化值设为1,那初始化值是为1吗?**答安不是,值为16843009。**因为初始是按照字节来处理的,int的大小为4个字节,所以每个字节的二进制为01,组合成十六进就是0x1010101,转换成十进制就是16843009。

  • memcpy

    int main() {
        char ch[] = {'H', 'e', 'l', 'l', 'o'};
        char *ch2 = "Hello";
        char *str = malloc(sizeof(ch) * 6);
        // 将ch2拷贝到str中,4为拷贝的个数
        //strcpy只拷贝字符串,memcpy拷贝一块内存
        memcpy(str, ch2, 4);
        // 源地址与目标地址不要出现重叠,可能会出现问题
    //    memcpy(&ch[2],ch,4);
        printf("%s\n", str);
        if (str) {
            free(str);
        }
        return EXIT_SUCCESS;
    }
    
  • memmove

    int main() {
        int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        // 源地址与目标地址不要出现重叠,可能会出现问题
    //    memcpy(&arr[2], arr, 20);
        // 但可以使用memmove, 拷贝重叠内存地址不会出现问题,
        // 但效率比较低,如果内存地址没有重叠的话,与memcpy一样。
        memmove(&arr[2], arr, 20);
        for (int i = 0; i < 10; ++i) {
            printf("%d\n", arr[i]);
        }
        return EXIT_SUCCESS;
    }
    
  • memcmp

    int main() {
        int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int arr1[] = {1, 2, 3, 4, 5};
        // 比较两块内存中前n个字节
        printf(!memcmp(arr, arr1, 20) ? "相同" : "不相同");  // 相同
        return EXIT_SUCCESS;
    }