内存分布、内存处理函数 --- 2021.4.16

目录

      • 内存分布:
      • 内存处理函数:
      • 结束语

内存分布:

首先我想说下为啥我会在这里讲内存分布这一概念呢,因为在实际应用场景中,我们其实常常会遇到内存如何分配的问题,举个例子,在程序中定义的变量,程序会自动为这些变量分配内存空间,并且如果清楚内存分布后,会对后面学习操作系统有所帮助。

并且在讲解内存分布之前,我想先引入几个概念,分别如下:

  1. 作用域
  2. 普通局部变量
  3. 静态局部变量
  4. 全局变量
  5. 静态全局变量
  6. 静态函数

作用域: 指的是变量起作用的范围,即在程序运行中,该变量的作用时间周期。

普通局部变量: 一般是 在{}范围之内定义的变量,该变量的作用域一般是在定义变量的{}之内有效。生命周期是程序运行至变量定义处开辟空间,所在的函数结束之后释放空间。

静态局部变量:{}范围之内定义的变量,前面加上static修饰变量。 该变量的作用域一般是在定义变量的{}之内有效。生命周期是执行main函数之前就已经开辟空间,程序结束之后才释放空间。

全局变量: 在函数之外定义的变量。 该变量的作用域是整个工程,所有文件。生命周期是执行main函数之前就已经开辟空间,程序结束之后才释放空间。

静态全局变量: 在函数之外定义的变量 ,加上static修饰的变量。该变量的作用域是当前文件。生命周期是执行main函数之前就已经开辟空间,程序结束之后才释放空间。

静态函数: 在函数定义时加上static修饰的函数,静态函数只可以被当前文件函数调用。

那在之前我们也讲到了程序里的这些变量都会提前内存分配好,那这些变量的内存分布具体是什么样子的呢?为了让大家更加直观的理解,特意画了一张图供大家观看。

首先假如我们在程序中有这么一段代码,定义了不同类型的代码,代码如下:

int e;
static int f;
int g= 10;
static int h = 10;
int main ()
{
     
	int a;
	int b = 10;
	static int c;
	static int d = 10;
	char *i = "test";
	char *k = NULL;
}

内存分布、内存处理函数 --- 2021.4.16_第1张图片
对上图分析如下:

  1. 首先我们可以看到,在程序中,内存分布一般都会分成栈区、堆区、静态全局区、文字常量区和代码区。
  2. 代码区顾名思义就是用来存放代码的地方。
  3. 在上述代码中,我们可以看到,代表文字常量的就是字符串“test”,所以就会存放在文字常量区。
  4. 静态全局区又分为两个部分一个部分是未初始化的静态全局,一般称之为bss区。另一部分是初始化的静态全局,一般称之为data区。同时静态全局一般包括普通静态局部变量、全局变量和静态全局变量。在上述代码中,变量g是在函数{}外面定义的,所以为全局变量,变量h也是在函数{}外面定义的,并且前面加了关键字static,所以为静态全局变量。变量d是在函数{}里面定义的,并且前面加了关键字static,所以为普通静态局部变量。同时变量g,h,d都是已近初始化的值,值为10
  5. 同理,变量efc都是未初始化的变量,所以就放在了bss区。
  6. 程序的栈区大小一般都是固定的。用来存放普通的局部变量。
  7. 堆区针对于内存不够的情况,当栈区存不下时,一般使用malloc函数来申请堆区空间。具体使用方法会在下一讲中提到,敬请关注。

内存处理函数:

1.memset函数

#include 
void *memset(void *s, int c, size_t n);
功能:将s的内存区域的前n个字节以参数c填入
参数:
       s:需要操作内存s的首地址
       c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
       n:指定需要设置的大小
返回值:s的首地址

举个例子:

int main()
{
     
        int  a = 10;
        memset(&a,0,sizeof(a));
        printf("a=%d\n",a);
        system("pause");
        return 0;
}

运行结果如下:
内存分布、内存处理函数 --- 2021.4.16_第2张图片
对上述代码分析如下:
首先我们定义了一个整型变量a,并且赋值为10。此时我们想要通过memset函数来修改a的值为0。从上述的memset函数的定义说明来看,需要操作的内存地址为&a,填充的字符为0,并且需要设置的大小为a的字节大小。并且字节大小为sizeof(a)

2.memcpy函数

#include 
void *memcpy(void *dest, const void *src, size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
       dest:目的内存首地址
       src:源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错
       n:需要拷贝的字节数
返回值:dest的首地址

举个例子:将数组a中前5个元素拷贝至数组b

int main()
{
        int  a[10] = {1,2,3,4,5,6,7,8,9,10};
        int  b[10] = { 0 };
        memcpy(b,a,sizeof(int)*5);
        for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++)
        {
               printf("%d ",b[i]);     
        }
        system("pause");
        return 0;
}

运行结果如下:
内存分布、内存处理函数 --- 2021.4.16_第3张图片

对上述代码分析如下:
在这里想要特别说明memcpy(b,a,sizeof(int)*5);这个部分,如果我们把这个函数部分需要拷贝的字节数修改成5会怎样,结果如下:
内存分布、内存处理函数 --- 2021.4.16_第4张图片结果如上所述,结果并不是我们想要的结果。因为我们定义的数组a的数组类型为int类型。int类型的字节大小是4个字节,所以最多也就能打印数组a中的12。所以如果我们想要使用memcpy函数将将数组a中前5个元素拷贝至数组b中,所以自然而然需要拷贝的总字节数为4* 5 = 20。那正如上述分析,我们再次试试,如果我们把这个函数部分需要拷贝的字节数修改成20会怎样,结果如下:
内存分布、内存处理函数 --- 2021.4.16_第5张图片3.memcmp函数

#include 
int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2所指向内存区域的前n个字节
参数:
       s1:内存首地址1
       s2:内存首地址2
       n:需比较的前n个字节
返回值:
       相等:=0
       大于:>0
       小于:<0

举个例子:

int main()
{
     
        char num1[] = {
      1,0,3,4,5,6,7 };
        char num2[] = {
      1,0,3,6,5,6,7 };
        printf("%d\n", memcmp(num1,num2,7*sizeof(char)));
        system("pause");
        return 0;
}

运行结果如下:
内存分布、内存处理函数 --- 2021.4.16_第6张图片

结束语

如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!

你可能感兴趣的:(C)