C01--C语言中内存管理的基本知识

1.内存相关概念

内存(Memory)是计算机的重要部件,也称内存储器和主存储器,它用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。

对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作

1.1cpu是什么

中央处理器(central processing unit,简称CPU)作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。CPU自产生以来,在逻辑结构、运行效率以及功能外延上取得了巨大发展。

2.C语言中几个概念

在编写C语言代码的过程中,我们通常都需要去定义一些变量,函数体,在学习的初期,我们只需要保证定义的内容格式没问题即可,并不需要关心这些内容到底是怎么存储的,可是随着学习的深入,关于变量,函数等的存储,我们必须要搞清楚到底是怎么存储的,这样写出来的代码效率才会更高,同时也会避免很多,只有用玄学两个字才能概括的现象。

2.1 变量

  • 全局变量(外部变量):出现在代码块{}之外的变量就是全局变量。
  • 局部变量(自动变量):一般情况下,代码块{}内部定义的变量就是自动变量。
  • 静态变量:是指内存位置在程序执行期间一直不改变的变量,用关键字static修饰。代码块内部的静态变量只能被这个代码块内部访问,代码块外部的静态变量只能被定义这个变量的文件访问。

2.2作用域

通常指的是变量的作用域,广义上讲,也有函数作用域及文件作用域等。我理解的作用域就是指某个事物能够存在的区域或范围,比如一滴水只有在0-100摄氏度之间才能存在,超出这个范围,广义上讲的“水”就不存在了,它就变成了冰或气体。

2.3函数

注意:C语言中函数默认都是全局的,可以使用static关键字将函数声明为静态函数(只能被定义这个函数的文件访问的函数)。

3.内存的分区

在这里插入图片描述

3.1代码区

程序被操作系统加载到内存时,所有可执行的代码被加载到代码区,也叫代码段,程序运行这段时间该区域数据不可被修改只可以被执行。

注意:"int a = 0;"语句可拆分成"int a;“和"a = 0”,定义变量a的"int a;"语句并不是代码,它在程序编译时就执行了,并没有放到代码区,放到代码区的只有"a = 0"这句。

3.2静态区

程序被加载到内存时就已经分配好,程序退出时才从内存中消失。存储静态变量和全局变量。代码执行期间一直占用内存!

3.3栈区

栈(stack)是一种先进后出的内存结构,所有的自动变量、函数形参都存储在栈中,这个动作由编译器自动完成,我们写程序时不需要考虑。栈区在程序运行期间是可以随时修改的。当一个自动变量超出其作用域时,自动从栈中弹出。

  • 每个线程都有自己专属的栈;
  • 栈的最大尺寸固定,超出则引起栈溢出;
  • 变量离开作用域后栈上的内存会自动释放。

示意图:注意地址顺序
在这里插入图片描述


//观察代码区、静态区、栈区的内存地址
#include "stdio.h"

int n = 0;

void test(int a, int b)
{  printf("形式参数a的地址是:%p\n形式参数b的地址是:%p\n",&a, &b);
}
int main(int argc, char* argv[])
{  static int m = 0;  int a = 0;  int b = 0;  
  printf("自动变量a的地址是:%p\n自动变量b的地址是:%p\n", &a, &b);  
  printf("全局变量n的地址是:%p\n静态变量m的地址是:%p\n", &n, &m);  
  test(a, b);  
  printf("main函数的地址是:%p", &main);  
  getchar();
}

运行结果:
自动变量a的地址是:00000022B8AFF8D4
自动变量b的地址是:00000022B8AFF8F4
全局变量n的地址是:00007FF62785D198
静态变量m的地址是:00007FF62785D19C
形式参数a的地址是:00000022B8AFF8B0
形式参数b的地址是:00000022B8AFF8B8
main函数的地址是:00007FF627851267

另外,栈不会很大,一般都是以K为单位。如果在程序中直接将较大的数组保存在函数内的栈变量中,很可能会内存溢出,导致程序崩溃,严格来说应该叫栈溢出(当栈空间已满,但还往栈内存压变量,这个就叫栈溢出)。

int _tmain(int argc, _TCHAR* argv[])
{
    char array_char[1024*1024*1024] = {0};
    array_char[0] = 'a';
    printf("%s", array_char);
    getchar();
}

visual studio19中报的警告:
严重性代码说明项目文件行禁止显示状态
警告C6262函数使用了堆栈的“1073741836”个字节: 超过了 /analyze:stacksize '16384'。 请考虑将某些数据移到堆中。  mydemo  D:\richangtest\c_test\mydemo\mydemo\test.c  4  

4.堆区

堆(heap)和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。更重要的是堆是一个大容器,它的容量要远远大于栈,这可以解决上一节实验造成的内存溢出困难。一般比较复杂的数据类型都是放在堆中。但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。对于一个32位操作系统,最大管理管理4G内存,其中1G是给操作系统自己用的,剩下的3G都是给用户程序,一个用户程序理论上可以使用3G的内存空间。堆上的内存必须手动释放(C/C++),除非语言执行环境支持GC(如C#在.NET上运行就有垃圾回收机制)。那堆内存如何使用?

4.1 malloc与free函数:

void *malloc(size_t size)
参数:

size -- 内存块的大小,以字节为单位。
返回值:
该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULLvoid *calloc(size_t nitems, size_t size)
参数:
nitems--元素个数
size--元素大小
返回值:
该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULLvoid *realloc(void *ptr, size_t size)
参数:
ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
返回值:
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULLvoid free(void *ptr)
参数:
ptr -- 指针指向一个要释放内存的内存块

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
   char *str;
 
   /* 最初的内存分配 */
   str = (char *) malloc(15);
   strcpy(str, "runoob");
   printf("String = %s,  Address = %p\n", str, str);
 
   /* 重新分配内存 */
   str = (char *) realloc(str, 25);
   strcat(str, ".com");
   printf("String = %s,  Address = %p\n", str, str);
 
   /* 释放已分配的内存 */
   free(str);
 
   return(0);
}

5.学习内存管理的目的

学习内存管理就是为了知道日后怎么样在合适的时候管理我们的内存。那么问题来了?什么时候用堆什么时候用栈呢?一般遵循以下三个原则:

  • 如果明确知道数据占用多少内存,那么数据量较小时用栈,较大时用堆;
  • 如果不知道数据量大小(可能需要占用较大内存),最好用堆(因为这样保险些);
  • 如果需要动态创建数组,则用堆。

操作系统在管理内存时,最小单位不是字节,而是内存页(32位操作系统的内存页一般是4K)。比如,初次申请1K内存,操作系统会分配1个内存页,也就是4K内存。4K是一个折中的选择,因为:内存页越大,内存浪费越多,但操作系统内存调度效率高,不用频繁分配和释放内存;内存页越小,内存浪费越少,但操作系统内存调度效率低,需要频繁分配和释放内存。嵌入式系统的内存内存资源很稀缺,其内存页会更小,因此在嵌入式开发当中需要特别注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值