c语言各种内存方式复习

news/2024/7/20 13:40:09 标签: 内存管理, 堆栈, 数据结构

c语言各种内存方式复习

  • 一、整体简介
      • 1、内存介绍
      • 2、内存区域的简介
      • 3、c语言内存的分配方式
      • 4、堆和栈的简介
  • 二、Ubuntu环境下的程序编译
      • 1、程序1
      • 2、程序2
  • 三、keil编译并通过上位机展示
      • 1、主函数代码
      • 2、编译所需的文件
      • 3、执行后的效果图
      • 4、查看地址分配
  • 四、总结

一、整体简介

1、内存介绍

C语言在内存中一共分为如下几个区域,分别是:

  1. 内存栈区: 存放局部变量名;
  2. 内存堆区: 存放new或者malloc出来的对象;
  3. 常数区: 存放局部变量或者全局变量的值;
  4. 静态区: 用于存放全局变量或者静态变量;
  5. 代码区:二进制代码。
    知道如上一些内存分配机制,有助于我们理解指针的概念。

C/C++不提供垃圾回收机制,因此需要对堆中的数据进行及时销毁,防止内存泄漏,使用free和delete销毁new和malloc申请的堆内存,而栈内存是动态释放。

2、内存区域的简介

一个由c/C++编译的程序内存分为以下几个部分

  • 1、栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  • 2、堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
  • 3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(RW), 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(ZI)。程序结束后有系统释放 。
  • 4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 (RO)
  • 5、程序代码区—存放函数体的二进制代码。 (RO)

3、c语言内存的分配方式

  • 1、静态存储区分配
    内存分配在程序编译之前完成,且在程序的整个运行期间都存在,例如全局变量、静态变量等。

  • 2、栈上分配
    在函数执行时,函数内的局部变量的存储单元在栈上创建,函数执行结束时这些存储单元自动释放。

  • 3、堆上分配
    堆分配(又称动态内存分配)。程序在运行时用malloc或者new申请内存,程序员自己用free或者delete释放,动态内存的生存期由我们自己决定。

4、堆和栈的简介

栈:主要存放基本类型数据(byte、short、int、long、float、double、char、boolean)和对象的引用变量。

优点:存取速度比较快,仅次于寄存器。
缺点:存在栈中的数据大小与生存期必须确定,缺乏灵活性。

堆:主要存放对象实体,是一个运行时数据区,类的对象和数组从中分配空间(通过new、new array等指令建立)。

优点:不需要程序代码显式的释放,堆是由垃圾回收机制来负责的;可以动态的分配内存大小。
缺点:由于要在运行时动态的分配内存,存取速度较慢。

常量池:放public static final定义的常量
静态域:存放静态成员(static定义的)

二、Ubuntu环境下的程序编译

1、程序1

在这里插入图片描述
代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
void before()
{
 
}
 
char g_buf[16];
char g_buf2[16];
char g_buf3[16];
char g_buf4[16];
char g_i_buf[]="123";
char g_i_buf2[]="123";
char g_i_buf3[]="123";
 
void after()
{
 
}
 
int main(int argc, char **argv)
{
        char l_buf[16];
        char l_buf2[16];
        char l_buf3[16];
        static char s_buf[16];
        static char s_buf2[16];
        static char s_buf3[16];
        char *p_buf;
        char *p_buf2;
        char *p_buf3;
 
        p_buf = (char *)malloc(sizeof(char) * 16);
        p_buf2 = (char *)malloc(sizeof(char) * 16);
        p_buf3 = (char *)malloc(sizeof(char) * 16);
 
        printf("g_buf: 0x%x\n", g_buf);
        printf("g_buf2: 0x%x\n", g_buf2);
        printf("g_buf3: 0x%x\n", g_buf3);
        printf("g_buf4: 0x%x\n", g_buf4);
 
        printf("g_i_buf: 0x%x\n", g_i_buf);
        printf("g_i_buf2: 0x%x\n", g_i_buf2);
        printf("g_i_buf3: 0x%x\n", g_i_buf3);
 
        printf("l_buf: 0x%x\n", l_buf);
        printf("l_buf2: 0x%x\n", l_buf2);
        printf("l_buf3: 0x%x\n", l_buf3);
 
        printf("s_buf: 0x%x\n", s_buf);
        printf("s_buf2: 0x%x\n", s_buf2);
        printf("s_buf3: 0x%x\n", s_buf3);
 
        printf("p_buf: 0x%x\n", p_buf);
        printf("p_buf2: 0x%x\n", p_buf2);
        printf("p_buf3: 0x%x\n", p_buf3);
 
        printf("before: 0x%x\n", before);
        printf("after: 0x%x\n", after);
        printf("main: 0x%x\n", main);
 
        if (argc > 1)
        {
                strcpy(l_buf, argv[1]);
        }
        return 0;
}

效果图:
在这里插入图片描述

代码分析:

l_buf/l_buf2/l_buf3 ,直接定义,是由编译器自动分配的,存储在栈中
s_buf/s_buf2/s_buf3,定义static,编译器编译时分配内存。为全局变量,在全局初始化区
p_buf/p_buf2/p_buf3,定义指针变量,存储在栈中
p_buf/p_buf2/p_buf3他们指向的空间,通过malloc申请空间,存放在堆中
g_buf/g_buf2/g_buf3/g_buf4/,定义的为全局变量,在全局初始化区

2、程序2

在这里插入图片描述

代码如下:

#include <stdio.h>
#include <stdlib.h>
//定义全局变量
int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;
void output(int a)
{
	printf("hello");
	printf("%d",a);
	printf("\n");
}

int main( )
{   
	//定义局部变量
	int a=2;
	static int inits_local_c=2, uninits_local_c;
    int init_local_d = 1;
    output(a);
    char *p;
    char str[10] = "lyy";
    //定义常量字符串
    char *var1 = "1234567890";
    char *var2 = "qwertyuiop";
    //动态分配
    int *p1=malloc(4);
    int *p2=malloc(4);
    //释放
    free(p1);
    free(p2);
    printf("栈区-变量地址\n");
    printf("                a:%p\n", &a);
    printf("                init_local_d:%p\n", &init_local_d);
    printf("                p:%p\n", &p);
    printf("              str:%p\n", str);
    printf("\n堆区-动态申请地址\n");
    printf("                   %p\n", p1);
    printf("                   %p\n", p2);
    printf("\n全局区-全局变量和静态变量\n");
    printf("\n.bss段\n");
    printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
    printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
    printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
    printf("\n.data段\n");
    printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
    printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
    printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
    printf("\n文字常量区\n");
    printf("文字常量地址     :%p\n",var1);
    printf("文字常量地址     :%p\n",var2);
    printf("\n代码区\n");
    printf("程序区地址       :%p\n",&main);
    printf("函数地址         :%p\n",&output);
    return 0;
}


效果图:
在这里插入图片描述

  • 代码结果分析:Ubuntu在栈区和堆区的地址值都是从上到下依次增大的

三、keil编译并通过上位机展示

1、主函数代码

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "beep.h"
static unsigned int val1 = 1;        //data段
unsigned int val2 = 1;               //初始化的全局变量data段
unsigned int val3 ;                  //未初始的在bsss段
const unsigned int val4 = 1;         //常量在rodata段,只读
unsigned char Demo(unsigned int num)
{
	char var;               //栈区,123456,存放在常量区
	unsigned int num1=1;            //栈区
  static unsigned int num2=0;      //,data段
  const unsigned int num3 =7;       //栈区
	printf("val1:	0x%x\r\n",&val1);
	printf("val2:	0x%x\r\n",&val2);
	printf("val3:	0x%x\r\n",&val3);
	printf("val4:	0x%x\r\n",&val4);
	printf("var:	0x%x\r\n",&var);
	printf("num1:	0x%x\r\n",&num1);
	printf("num2	0x%x\r\n",&num2);
	printf("num3:	0x%x\r\n",&num3);
	return 1;
}
int main(void)
{		
	unsigned int num=0;
	BEEP_Init();				//初始化蜂鸣器不响
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200          
	num = Demo(num); //返回值存放在栈区
}

2、编译所需的文件

在这里插入图片描述

3、执行后的效果图

在这里插入图片描述

4、查看地址分配

在这里插入图片描述

  • 地址分配结论

    ROM的地址分配是从0x8000000开始,大小为0x80000,这个部分用于存放代码区和文字常量区。
    RAM的地址分配是从0x20000000开始,其大小是0x10000,这个区域用来存放栈、堆、全局区。

四、总结

通过这一次尝试,首先我们了解了内存的分类以及内存的分配方式,实际上单片机内存分配和c语言控制内存的方式差不多,都有类似的结构,总体来说,我们要运用好内存,毕竟内存有限,我们需要用不同的分配方式来高效的完成我们功能的实现,


http://www.niftyadmin.cn/n/1295874.html

相关文章

《CLR Via C# 第3版》笔记之(一) - CLR版本及编译平台

久闻这本书的大名&#xff0c;终于有中文版的了&#xff08;英文太差没办法&#xff09;。希望通过学习本书能够对CLR和.net有更深刻的了解&#xff0c;并且通过blog记录一些平时不太留意的地方和心得体。 主要内容&#xff1a; 通过CLRVer.exe查看本机的CLR版本编译平台对最终…

基于IIC的温度传感器实验

基于IIC的温度传感器实验一、IIC简介二、硬件IIC与软件IIC的区别三、实现I2CAHT20温湿度传感器的温度采集1、AHT20简介2、配置函数库&#xff0c;编写函数1&#xff1a;下载驱动函数2&#xff1a;修改相应引脚号3&#xff1a;将相应文件复制进去4&#xff1a;添加库文件5&#…

SQL Server 2008 I/O性能监控

原文首发于it168&#xff0c;链接见http://tech.it168.com/a2011/0221/1158/000001158998.shtml I/O性能诊断 SQL Server性能非常依赖于I/O子系统。除非你的数据库适合物理内存&#xff0c;SQL Server经常地会有数据库页面进出缓存池。这样就发生了实质的I/O流量。同样&#xf…

HAL库与标准库的理解

HAL库的学习HAL库和标准库的原理区别串口通信实验包含的文件区别具体代码区别总结HAL库和标准库的原理区别 HAL简介&#xff1a; HAL库 是st公司为了更方便地进行stm32之间的移植而开发的库&#xff0c;通用性很强&#xff0c;在不同的两款stm32芯片之间的移植基本上不需要修…

网站的搭建

今天老师交我们用Linux搭建了BBS 、、、高兴转载于:https://blog.51cto.com/2969742/541823

基于arduino的STM32IDE串口实现

基于arduino的STM32串口通信实现arduino简介arduino的安装以及arduino_STM32的配置1、安装arduino2、下载[arduino_STM32](https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/master/STM32F1)3、拷贝相关文件4、配置arduino1、打开arduino选择STM32F12、下载ARM3、下…

详解JDBC与Hibernate区别

刚开始学习JAVA时&#xff0c;认为Hibernate是一个很神圣的东西&#xff0c;好像是会了SSH&#xff0c;就能走遍全世界一样。记得曾经在枫叶面试的时候&#xff0c;我们几个同 学出还说这个公司怎么这么的落后&#xff0c;还有JDBC&#xff0c;没有一点上进心。可是毕业以后才发…

画属于自己的STM32C8T6PCB电路板

STM32pcb封装及画制一、学会找封装并添加封装1、创建自己的元件封装库2、给原理图上的元件添加封装二、画PCB板1、生成PCB板并布局pcb排布2、最后生成的样子三、生成bom表和Gerber文件1、生成bom表2、生成Gerber文件四、总结一、学会找封装并添加封装 学会自己找封装的重要性 …