内存四区
代码区
作用:存放CPU执行的二进制机器指令
特点:
1.只读
2.共享
全局/静态区
特点:
- 全局/静态区存储全局变量、静态变量、常量,该区变量在程序运行期间一直存在
- 程序结束由系统回收。
- 已初始化的数据放在data段,未初始化的数据放到bss段
- 该区变量当未初始化时,会有有默认值初始化。
总结:
- 管理方式:编译器自动管理该区内存。
- 生命周期:程序结束释放
全局区详解:
- 全局变量:在函数内定义的变量是局部变量,而在函数外定义的变量称为全局变量;属于外部链接属性,可在外部文件中使用,用extern关键字声明 便可使用;未初始化的全局变量编译器一般默认初始化为0;
- 静态变量:static声明的变量;属于内部链接属性,只能在当前文件中使用;有时希望函数中的局部变量(属于自动变量)的值在函数调用结束后不被释放,在下一次再调用该函数时,该变量已有值(就是上一次函数调用结束时的值),这时就应该指定该局部变量为静态局部变量,用关键字static声明;
代码演示:
void func()
{
//int s_a = 10;//局部变量,属于自动(auto)变量函数调用结束自动释放,再次调用重新初始化
static int s_a = 10; //静态变量只初始化一次
s_a++;
printf("%d\n", s_a);
}
void test()
{
func();
func();
func();
}
int main()
{
test();
return 0;
}
不用static关键字声明的局部变量代码运行演示:
用static关键字声明的局部变量代码运行演示:
- 常量:一般常量及字符串常量和const关键字修饰的全局变量;const关键字详见个人博客《const修饰的变量和指针》
//全局变量
int g_a = 10;
int g_b = 10;
//全局常量
const int c_g_a = 10;
const int c_g_b = 10;
int main() {
//局部变量
int a = 10;
int b = 10;
//打印地址
cout << "局部变量a地址为: " << (int)&a << endl;
cout << "局部变量b地址为: " << (int)&b << endl;
cout << "全局变量g_a地址为: " << (int)&g_a << endl;
cout << "全局变量g_b地址为: " << (int)&g_b << endl;
//静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a地址为: " << (int)&s_a << endl;
cout << "静态变量s_b地址为: " << (int)&s_b << endl;
cout << "字符串常量地址为: " << (int)&"hello world" << endl;
cout << "字符串常量地址为: " << (int)&"hello world1" << endl;
cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;
const int c_l_a = 10;
const int c_l_b = 10;
cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;
cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;
system("pause");
return 0;
}
代码运行结果:
总结:
C++中在程序运行前分为全局区和代码区
代码区特点是共享和只读
全局区中存放全局变量、静态变量、常量
常量区中存放 const修饰的全局常量 和 字符串常量
栈区
特点:
- 栈是一种先进后出的内存结构,由编译器自动分配释放数据。
- 主要存放函数的形式参数值、局部变量等。
- 函数运行结束,相应栈变量会被自动释放
- 栈空间较小,不适合将大量数据存放在栈中
总结:
- 管理方式:编译器自动管理该区内存。
- 空间大小:提前规定、较小。
- 生命周期:函数使用完毕立即释放。
注意事项:
不要返回局部变量的地址
#include <stdio.h>
int * func()
{
int a = 10;//局部变量a,函数执行完后释放
return &a;//返回变量a的地址
}
void test01()
{
int *p = func();
printf("a = %d\n",*p);//打印输出乱码
}
int main()
{
test01();
return 0;
}
原因是局部变量是存放在栈区的,随着函数调用结束栈区空间就被释放了,如果再次访问,属于非法;某些编译器可以正常打印变量a的值,是因为编译器做了保留;
#include <stdio.h>
char * getString()
{
char str[] = "hello world";//字符串常量是放在常量区,当一个字符串以数组形式声明时,调用该函数时会拷贝一份到栈区,数组名存放字符串首地址
return str;//返回的数组名str属于局部变量
}
void test02()
{
char *p = NULL;
p = getString();
printf("p = %s\n",p); //乱码;linux下发生段错误,返回的str由p接受,p属于野指针;
}
int main()
{
test02();
return 0;
}
堆区
特点:
- 堆区由开发人员手动申请和释放,在释放之前,该块堆空间可一直使用。
- 由程序员分配和释放,若程序员不释放,程序结束时由系统回收内存。
- 堆空间一般没有软限制,只受限于硬件。会比栈空间更大,适宜存放较大数据。
总结:
- 管理方式:开发人员手动申请和释放。
- 空间大小:较大。
- 生命周期:手动释放之前一直存在,或程序结束由系统
回收。
堆区基本使用:
由开发人员手动开辟的空间;如常用malloc申请空间,free释放空间;
#include <stdio.h>
#include <stdlib.h>
int * getSpace()
{
int *p= malloc(sizeof(int)*5);//malloc括号中内容应尽量体现出申请内存的用途
if(p == NULL){
printf("申请内存失败\n");
return 1;
}
for(int i=0;i<5;i++){
p[i] = i+1;
}
return p;
}
void test01()
{
int *p = getSpace();
for(int i=0;i<5;i++){
printf("%d\n",p[i]);
}
if(p != NULL){
free(p);//手动开辟,手动释放
p = NULL;//对指针变量p做置空操作
}
}
int main()
{
test01();
return 0;
}
注意事项:
主调函数中没有分配内存的空指针,被调函数形参中同级指针无法修饰主调函数中的实参
- 错误代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void allocateSpace(char *pp)
{
char * temp = malloc(sizeof(char)*20);
memset(temp,0,20);
strcpy(temp,"Hello World!");
pp = temp;//指针pp指向Hello World首地址;但是函数执行前后并没有修改主调函数中指针变量p的值
}
void test01()
{
char * p = NULL;
allocateSpace(p);
printf("%s\n", p);//打印输出NULL;
}
int main()
{
test01();
return 0;
}
内存分析图解:
上述问题解决方案:
主调函数中没有分配内存的空指针,被调函数形参中要用高级指针修饰主调函数中的实参
解决方案一:
//解决方案1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void allocateSpace(char **pp) //二级指针接受一级指针p的地址;传参的过程也就是赋值的过程,相当于pp = &p
{
char * temp = malloc(sizeof(char)*20);
memset(temp,0,20);
strcpy(temp,"Hello World!");
*pp = temp;//*pp = p;通过*pp解引用间接的将字符串Hello Wold的首地址赋值给主调函数中一级指针p
}
void test01()
{
char * p = NULL;
allocateSpace(&p);
printf("%s\n", p);
}
int main()
{
test01();
return 0;
}
- 内存分析图解:
解决方案二:
//解决方案2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * allocateSpace()
{
char * temp = malloc(sizeof(char)*20);
memset(temp,0,20);
strcpy(temp,"Hello World!");
return temp;//返回字符串Hello World的首地址
}
void test01()
{
char * p = NULL;
p = allocateSpace();//字符型指针变量接受函数返回值,相当于指针p指向了字符串Hello World的首地址;
printf("%s\n", p);
}
int main()
{
test01();
return 0;
}
- 内存分析图解:
- 解决后代码运行结果: