想要构建一个算法工程,尤其是落地到设备端,C语言显然是不二之选,而内存是我们学习C语言永远绕不过的一道坎。但事实上,内存并没有我们想象中的那么可怕。本文中,我们姑且把内存分为四个区域:代码区,常量区,堆区,栈区。
简单介绍下内存四区:
1.代码区:该内存用于存放代码段。
2.常量区:该内存用于存放各类常量,如字符串常量等。
3.堆区:该内存用于存放用户自己开辟的空间,用户不手动释放,该内存就一直存在。(好用的同时需要注意内存泄漏)
4.栈区:该内存用于存放临时变量和函数入口地址,编译器自动分配自动释放。
![]()
1.常量区
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char * getStr1()
{
char *p1 = "YLYL";
return p1;
}
char *getStr2()
{
char *p2 = "YLYL";
return p2;
}
void main()
{
char *p1 = NULL;
char *p2 = NULL;
p1 = getStr1();
p2 = getStr2();
//打印p1 p2指针变量 所指向内存空间的数据
printf("p1:%s , p2:%s \n", p1, p2);
//打印p1 p2 指针的值
printf("p1:%d , p2:%d \n", p1, p2);
return ;
}

本文通过字符串常量来介绍下常量区的特征,从图中我们可以总结出指针的两个要点:
1.指针的指向哪里,就把哪里的地址赋值给指针变量。
2.指针变量的值,和指针指向的内存空间的值是完全不同的概念。如上图中,指针变量的值是0x1314,而指针指向的内存空间的值是YLYL。
通过打印指针变量的值,我们发现两个函数中相同的字符串常量在内存空间中有着相同的地址(这其实是编译器在编译阶段所做的优化)。
2.堆区
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//利用malloc分配内存
char *getMem(int num)
{
char *p1 = NULL;
p1 = (char *)malloc(sizeof(char) * num);
if (p1 == NULL)
{
return NULL;
}
return p1;
}
void main()
{
char *tmp = NULL;
tmp = getMem(10);
if (tmp == NULL)
{
return ;
}
strcpy(tmp, "YLYL"); //向tmp做指向的内存空间中copy数据
printf("tmp:%s.\n", tmp);
return ;
}

如上述代码和示意图所示,P1指针变量是临时变量,所以定义在栈空间上,因此getMem函数退出后,P1的空间将会被自动释放掉,但是在释放的同时,P1指针变量的值先一步赋值给了tmp指针变量,而该指针变量所指向的是堆内存,因此不会被释放,从而实现了内存空间的创建,这就是堆空间的特性~
3.栈区
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *getMem(int num)
{
char *p1 = NULL;
p1 = (char *)malloc(sizeof(char) * num);
if (p1 == NULL)
{
return NULL;
}
return p1;
}
char *getMem_stack()
{
char buf[64]; //临时变量 栈区存放
strcpy(buf, "YLYL_2");
return buf;
}
void main()
{
char *tmp = NULL;
tmp = getMem(10);
if (tmp == NULL)
{
return ;
}
strcpy(tmp, "YLYL_1"); //向tmp做指向的内存空间中copy数据
tmp = getMem_stack();
printf("tmp:%s.\n", tmp);
return ;
}

如上述代码和示意图所示,buf数组是临时变量,所以定义在栈空间上,因此getMem函数退出后,buf首地址所指向的空间将会被自动释放掉,存储的值也就相应的被释放了。因此即使,buf首地址先一步赋值给了tmp指针变量,但也无法获取对应的值,这就是栈空间的特性~
网友评论