内存总体被分为四大区域:堆区、栈区、全局区、代码区。每个区所存放的内容以及内容的声明周期不同。将内存进行区域划分有利于内存管理,从而提高对数据的处理效率。
A. 全局区
全局区也称为静态区,存放全局变量和静态变量,其中初始化的全局变量和静态变量存放在同一区块,未初始化的全局变量和静态变量存放在相邻的区块,程序结束后,由系统自动释放。
1. 样例分析
char * getStr1() {
char *fp1 = "abcd";
return fp1;
};
char * getStr2() {
char *fp2 = "abcd";
return fp2;
}
int main() {
char *p = getStr1();
char *q = getStr2();
printf("p:%s, p:%p\n", p, p);
printf("q:%s, q:%p\n", q, q);
return 0;
}
2. 打印结果
p:abcd, p:0x100b68f8e
q:abcd, q:0x100b68f8e
3. 内存分配图
4. 分析:
1、进入main
函数
2、在栈中创建char *
变量p
3、进入getStr1
方法
4、在栈中创建char *
变量fp1
5、字符串abcd
属于静态变量,存储在全局区,且由系统进行释放。通过打印得知其地址值为0x100b68f8e
6、将字符串的地址赋值给指针变量fp1
,那么变量fp1
则指向字符串所在的内存
7、返回fp1
的指针,并将该指针赋值给变量p
,getStr1
方法结束,fp1
变量销毁,变量p
指向字符串的所在的内存
8、创建并赋值变量q
的方式同于步骤2
至步骤7
因此最后打印的结果是变量p
内存存放的值与q
相同,p
指向的地址值也与q
相同。
B. 栈区:
栈区由编译器自动分配释放局部变量和函数参数的值。
1. 样例分析
char * getStr3() {
char str[] = "abcd";
return str;
};
int main() {
char *strp = getStr3();
printf("strp:%s, strp:%p", strp, strp);
return 0;
}
2. 打印结果
strp:�M�y��7����, strp:0x7ffee691376b
3. 绘制内存图
4. 分析:
1、进入main
函数
2、栈中创建strp
变量
3、进入getStr3
函数
4、栈中创建str
变量
5、全局区存入字符串abcd
6、将全局区的字符串复制一份赋值给str
变量
7、返回str
变量的地址,getStr3
函数运行结束,str
变量释放,将str
的地址值赋值给变量strp
,strp
指向str
的内存
8、因str
的内存已经被释放,所以str
内存中的内容未知,故而乱码
C. 堆区分析
堆区由程序员手动创建和释放,若程序员未释放对应的变量,那么在程序结束后可能会被系统释放。
1. 样例分析
char * getStr4() {
char *str = (char *)malloc(100);
printf("str: %p\n", str);
if (str == NULL) {
return NULL;
}
strcpy(str, "abcd");
return str;
}
int main() {
char *strp1 = getStr4();
if (strp1 != NULL) {
printf("strp1:%s, strp1:%p\n", strp1, strp1);
free(strp1);
strp1 = NULL;
}
return 0;
}
2. 打印结果:
str: 0x7fd490c02a70
strp1:abcd, strp1:0x7fd490c02a70
3. 内存图绘制
4. 分析
1、进入main
函数
2、栈区创建strp1
变量
3、进入getStr4
函数
4、栈区创建str
变量
5、堆区分配内存,并将分配好的内存地址赋值给str
变量,str
指向分配好的堆内存
6、全局区分配字符串abcd
的内存,并将其值复制一份赋值给堆中分配的内存
7、返回str
变量的值,getStr4
函数结束,str
变量释放,并将值赋值给strp1
,strp1
指向堆中分配的内存
8、打印结束后,释放堆中的内存
D. 栈的生长方向和内存存放方向
栈的生长方向:高字节 -> 低字节
堆的生长方向:低字节 -> 高字节
内存的存放方向: 低字节 -> 高字节