C/C++中的内存四区

内存四区

在这里插入图片描述

代码区

作用:存放CPU执行的二进制机器指令
特点:
1.只读
2.共享

全局/静态区

特点:

  • 全局/静态区存储全局变量、静态变量、常量,该区变量在程序运行期间一直存在
  • 程序结束由系统回收。
  • 已初始化的数据放在data段,未初始化的数据放到bss段
  • 该区变量当未初始化时,会有有默认值初始化。

总结:

  • 管理方式:编译器自动管理该区内存。
  • 生命周期:程序结束释放

全局区详解:

  1. 全局变量:在函数内定义的变量是局部变量,而在函数外定义的变量称为全局变量;属于外部链接属性,可在外部文件中使用,用extern关键字声明 便可使用;未初始化的全局变量编译器一般默认初始化为0;
  2. 静态变量: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关键字声明的局部变量代码运行演示:
在这里插入图片描述

  1. 常量:一般常量及字符串常量和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;
}
  • 内存分析图解:
    在这里插入图片描述
  • 解决后代码运行结果:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值