一、C程序的内存分配
1.全局变量
全局变量也就是编程术语中的一种,也称为外部变量,它是在函数外部定义的变量,也可以是在本程序任何地方创建。此外,变量分为局部与5261全局,局部变量又可被叫做内部的变量。是由某对象或某个函数所创建的变量通常都是局部变量,只能被内部引用。
我们知道,全局变量是C语言语法和语义中一个很重要的知识点,首先它的存在意义需要从三个不同角度去理解:对于程序员来说,它是一个记录内容的变量(variable);对于编译/链接器来说,它是一个需要解析的符号(symbol);对于计算机来说,它可能是具有地址的一块内存(memory)。其次是语法/语义:从作用域上看,带static关键字的全局变量范围只能限定在文件里,否则会外联到整个模块和项目中;从生存期来看,它是静态的,贯穿整个程序或模块运行期间(注意,正是跨单元访问和持续生存周期这两个特点使得全局变量往往成为一段受攻击代码的突破口,了解这一点十分重要);从空间分配上看,定义且初始化的全局变量在编译时在数据段(.data)分配空间,定义但未初始化的全局变量暂存(tentative definition)在.bss段,编译时自动清零,而仅仅是声明的全局变量只能算个符号,寄存在编译器的符号表内,不会分配空间,直到链接或者运行时再重定向到相应的地址上。
全局变量的使用注意事项如下: 1、使用全局变量程序运行时速更快。 2、对于局部变量的名字空间污染,这个在不使用太多变量时是可以避免的。 3、当全局变量与局部变量重名的时候,起作用的是局部变量。 4、还可以用extern在函数外对全局变量声明,使全局变量的作用域从声明处到文件的结束。
2.局部变量
定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。
几点说明:
-
在 main 函数中定义的变量也是局部变量,只能在 main 函数中使用;同时,main 函数中也不能使用其它函数中定义的变量。main 函数也是一个函数,与其它函数地位平等。 -
形参变量、在函数体内定义的变量都是局部变量。实参给形参传值的过程也就是给局部变量赋值的过程。 -
可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰,也不会发生混淆。 -
在语句块中也可定义变量,它的作用域只限于当前语句块。
3.堆
堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。
堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
- 堆中某个结点的值总是不大于或不小于其父结点的值;
- 堆总是一棵完全二叉树。
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减;
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
4.栈
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
在函数被调用时,其参数会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中;
由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场;
从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
5.C程序的内存分配
一个典型的C程序存储分区包含以下几类:
Text段通常也称为代码段,由可执行指令构成,是程序在目标文件或内存中的一部分,Text段通常放在栈或堆的下面,以防止堆栈溢出篡改其数据。 通常情况下,Text段是可共享的,对于需要频繁调用的程序,其在内存中只需要一份拷贝即可,如文本编辑器、C编译器、Shell等,因此text段通常设为只读以防止程序的突发性的修改。
已初始化数据段,通常简单称作数据段,数据段占据程序虚拟地址空间的一部分,内部包括全局变量、静态变量(程序负责初始化这些变量)。需注意的是,数据段不是只读的,在运行时变量值是可以变动的。 数据段还可以更细的分为初始化只读区以及初始化可读写区。
二、Ubuntu和STM32下对C程序输出信息进行验证
1.Ubuntu
在虚拟机中打开Ubuntu,然后自定义路径,在终端中输入如下命令: gedit test.c 该命令为新建一个test.c文件
然后在文件中复制如下代码:
#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;
}
然后使用命令: gcc test.c进行编译
输入ls可以看到有一个绿色的a.out可执行文件。 然后我们输入./a.out命令进行执行 如图可以看到栈区的变量地址和堆区的变量地址。 可以看到栈区的地址和堆区的地址就是逐渐变大。
2.在STM32上查看
首先需要用到串口初始化的工程,这里建立工程的步骤就不详细介绍了,这里给出usart.c和usart.h文件的相关代码: usart.c
#include "usart.h"
#if 1
#pragma import(__use_no_semihosting)
struct __FILE {
int handle;
};
FILE __stdout;
void _sys_exit(int x){
x = x;
}
int fputc(int ch, FILE *f){
while((USART_n->SR&0X40)==0);
USART_n->DR = (u8) ch;
return ch;
}
#endif
void _ttywrch(int ch)
{
ch = ch;
}
#if EN_USART1
u8 USART1_RX_BUF[USART1_REC_LEN];
u16 USART1_RX_STA=0;
void USART1_Init(u32 bound){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
void USART1_IRQHandler(void){
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
Res =USART_ReceiveData(USART1);
printf("%c",Res);
if((USART1_RX_STA&0x8000)==0){
if(USART1_RX_STA&0x4000){
if(Res!=0x0a)USART1_RX_STA=0;
else USART1_RX_STA|=0x8000;
}else{
if(Res==0x0d)USART1_RX_STA|=0x4000;
else{
USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ;
USART1_RX_STA++;
if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;
}
}
}
}
}
#endif
#if EN_USART2
u8 USART2_RX_BUF[USART2_REC_LEN];
u16 USART2_RX_STA=0;
void USART2_printf (char *fmt, ...){
char buffer[USART2_REC_LEN+1];
u8 i = 0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buffer, USART2_REC_LEN+1, fmt, arg_ptr);
while ((i < USART2_REC_LEN) && (i < strlen(buffer))){
USART_SendData(USART2, (u8) buffer[i++]);
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
}
va_end(arg_ptr);
}
void USART2_Init(u32 bound){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART2_IRQHandler(void){
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){
Res =USART_ReceiveData(USART2);
printf("%c",Res);
if((USART2_RX_STA&0x8000)==0){
if(USART2_RX_STA&0x4000){
if(Res!=0x0a)USART2_RX_STA=0;
else USART2_RX_STA|=0x8000;
}else{
if(Res==0x0d)USART2_RX_STA|=0x4000;
else{
USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
USART2_RX_STA++;
if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;
}
}
}
}
}
#endif
#if EN_USART3
u8 USART3_RX_BUF[USART3_REC_LEN];
u16 USART3_RX_STA=0;
void USART3_printf (char *fmt, ...){
char buffer[USART3_REC_LEN+1];
u8 i = 0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buffer, USART3_REC_LEN+1, fmt, arg_ptr);
while ((i < USART3_REC_LEN) && (i < strlen(buffer))){
USART_SendData(USART3, (u8) buffer[i++]);
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
}
va_end(arg_ptr);
}
void USART3_Init(u32 BaudRate){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = BaudRate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
USART_Cmd(USART3, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART3_IRQHandler(void){
u8 Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET){
Res =USART_ReceiveData(USART3);
if(Res=='S'){
USART3_RX_STA=1;
}else if(Res=='K'){
USART3_RX_STA=2;
}
}
}
#endif
usart.h
#ifndef __USART_H
#define __USART_H
#include <string.h>
#include "stdio.h"
#include "sys.h"
#define USART_n USART1
#define USART1_REC_LEN 200
#define USART2_REC_LEN 200
#define USART3_REC_LEN 200
#define EN_USART1 1
#define EN_USART2 0
#define EN_USART3 0
extern u8 USART1_RX_BUF[USART1_REC_LEN];
extern u8 USART2_RX_BUF[USART2_REC_LEN];
extern u8 USART3_RX_BUF[USART3_REC_LEN];
extern u16 USART1_RX_STA;
extern u16 USART2_RX_STA;
extern u16 USART3_RX_STA;
void USART1_Init(u32 bound);
void USART2_Init(u32 bound);
void USART3_Init(u32 bound);
void USART1_printf(char* fmt,...);
void USART2_printf(char* fmt,...);
void USART3_printf(char* fmt,...);
#endif
修改main.c文件为如下代码:
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#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 (void){
u8 a=7,b=8;
RCC_Configuration();
USART1_Init(115200);
while(1){
int a=2;
static int inits_local_c=2, uninits_local_c;
int init_local_d = 1;
output(a);
char *p;
char str[10] = "cleveryoga";
char *var1 = "1234567890";
char *var2 = "abcdefghij";
int *p1=malloc(4);
int *p2=malloc(4);
free(p1);
free(p2);
printf("栈区-变量地址\n\r");
printf(" a:%p\n\r", &a);
printf(" init_local_d:%p\n\r", &init_local_d);
printf(" p:%p\n\r", &p);
printf(" str:%p\n\r", str);
printf("\n堆区-动态申请地址\n\r");
printf(" %p\n\r", p1);
printf(" %p\n\r", p2);
printf("\n全局区-全局变量和静态变量\n\r");
printf("\n.bss段\n");
printf("全局外部无初值 uninit_global_a:%p\n\r", &uninit_global_a);
printf("静态外部无初值 uninits_global_b:%p\n\r", &uninits_global_b);
printf("静态内部无初值 uninits_local_c:%p\n\r", &uninits_local_c);
printf("\n.data段\n\r");
printf("全局外部有初值 init_global_a:%p\n\r", &init_global_a);
printf("静态外部有初值 inits_global_b:%p\n\r", &inits_global_b);
printf("静态内部有初值 inits_local_c:%p\n\r", &inits_local_c);
printf("\n文字常量区\n\r");
printf("文字常量地址 :%p\n\r",var1);
printf("文字常量地址 :%p\n\r",var2);
printf("\n代码区\n\r");
printf("程序区地址 :%p\n\r",&main);
printf("函数地址 :%p\n\r",&output);
delay_ms(1000);
}
}
编译成功后进行烧录,在串口助手打开得到如下结果: 上图结果可以看出栈区变量地址和堆区变量地址是逐渐变小的,与Ubuntu中看的的结果相反。 这是因为STM32中的存储位置不同。
RAM(随机存取存储器) 存储的内容可通过指令随机读写访问。RAM中的存储的数据在掉电是会丢失,因而只能在开机运行时存储数据。其中RAM又可以分为两种,一种是Dynamic RAM(DRAM动态随机存储器),另一种是Static RAM(SRAM,静态随机存储器)。栈、堆、全局区(.bss段、.data段)都是存放在RAM中。 ROM(只读存储器) 只能从里面读出数据而不能任意写入数据。ROM与RAM相比,具有读写速度慢的缺点。但由于其具有掉电后数据可保持不变的优点,因此常用也存放一次性写入的程序和数据,比如主版的BIOS程序的芯片就是ROM存储器。代码区和常量区的内容是不允许被修改的,所以存放于ROM中。
上图中可以看到IROM的起始位置是0x8000000,大小为010000,这个部分用于存放代码区和文字常量区。 IRAM的起始位置是0x20000000,大小是0x5000,这个区域用来存放栈、堆、全局区(.bss段、.data段)
三、总结
本次实验学习了C程序中内存分配相关的知识,同时更加深入巩固了,堆,栈,全局变量和局部变量的相关知识。
四、参考链接
https://www.cnblogs.com/miaoxiong/p/11021827.html https://blog.csdn.net/qq_43279579/article/details/110308101
|