温习全局常量、全局变量、局部变量、静态变量、堆、栈

温习全局常量、全局变量、局部变量、静态变量、堆、栈

引言

一、各种变量概念理解

1.常量

c++中常量修饰符为const,告诉编译器这个名字是常量。如果定义了某对象为常量,然后试图想修改它,编译器会报错的。

const int x=10;

注意:

1)在C中,编译器对待const如同变量一样,只不过带有一个特殊的标记,意思是:“不要改变我”。

2)在c++中,一个const常量必须有初始值。

2.全局变量

全局变量顾名思义就是在所有函数体(包括main函数)的外部定义的,程序的所有部分(甚至外部文件中的代码)都可以使用它的,这也就是说全局变量不受作用域的影响,总是可用的。

#include<iostream>
using namespace std;
int globe;//全局变量
static int satus;
void digui(int a)
{
	if(a<10)
	{
		cout << a << ' ';
		digui(a + 1);
	}
}
void main04()
{
	digui(2);
}

注意:

1)想在一个文件中使用另外一个文件定义的全局变量,可以用extern关键字,那么就可以在该文件使用了。

extern int globe
2)全局变量的生命周期一直到程序结束。

3.局部变量

局部变量出现在一个作用域内,它们是局限与一个函数的。因此常被称为自动变量,因为它们在进入作用域是自动生成,离开作用域时自动消失,常用关键字auto,但是局部变量默认为auto,所以没有必要声明auto。

4.静态变量

静态变量常用static关键字修饰,通常情况下,函数中定义的局部变量在函数作用域结束时消失。当再次调用这个函数时,再被创建,再被初始化,但是想局部变量的值在程序的整个生命周期都仍然存在,我们就把它定义为static静态变量,具有记忆的功能。

但是为啥不使用全局变量呢?static变量的优点在于在函数范围之外它是不可用的,所以不容易被修改。

static还可以作用于函数和外部变量,它的意思是“在文件的外部不可以使用这个名字”。函数名或者变量名是局限于文件的

5.外部变量

外部变量常用关键字为:extern,他告诉编译器存在一个变量和函数,即使在当前编译的文件中没看见他,但是可能存在当前文件的后面或者另外的一个文件。

6.内存分区

img

程序运行前 :

代码区(存放CPU执行的机器指令):

  1. 共享的,共享的目的是对于频繁被执行的程序,只需在内存中存放一份代码即可

  2. 只读的,使其只读的原因是防止程序以外地被修改了它的指令。

全局区(存放全局变量、静态变量、字符串常量、全局常量):
该区域的数据在程序结束后由操作系统释放。

程序运行后:

栈区(局部变量、形参):

  1. 由编译器自动分配和释放,存放函数的参数、局部变量等

  2. 注意:不要反悔局部变量的地址

堆区:

  1. 由程序员分配释放,如果程序员不释放,程序结束时由系统回收

  2. c++中利用new开辟内存,返回堆上的地址,用指针接收

二、变量存储区域介绍

  • C语言在内存中一共分为如下几个区域
区域作用
内存栈区存放局部变量名
内存堆区存放new或者malloc出来的对象
常数区存放局部变量或者全局变量的值
静态区用于存放全局变量或者静态变量
代码区二进制代码
内存相关的详细介绍
  1. 栈区(stack)–由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

  2. 堆区(heap)–般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式类似于链表。

  3. 全局区(静态区)(static)–全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(RW),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(ZI)。程序结束后有系统释放。 和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。

  4. 文字常量区–常量字符串就是放在这里的。程序结束后由系统释放 (RO)

  5. 程序代码区–存放函数体的二进制代码。 (RO)

  6. 常量存储区—和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量的存储区,并且在程序运行期间,存储区内的常量是全局可见的。这是一块比较特殊的存储去,他们里面存放的是常量,不允许被修改。

  7. 内存存在两种属性:静态分配内存和动态分配内存。
    静态分配内存:是在程序编译和链接时就确定好的内存。
    动态分配内存:是在程序加载、调入、执行的时候分配/回收的内存。

  8. Stack: 栈,存放Automatic Variables,按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。通常是用于那些在编译期间就能确定存储大小的变量的存储区,用于在函数作用域内创建,在离开作用域后自动销毁的变量的存储区。通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的。栈的大小是有限的,通常Visual C++编译器的默认栈的大小为1MB,所以不要定义int a[1000000]这样的超大数组。

  9. Heap: 堆,自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
    通常是用于那些在编译期间不能确定存储大小的变量的存储区,它的存储空间是不连续的,一般由malloc(或new)函数来分配内存块,并且需要用free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨着定义的指针变量,所指向的malloc出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达4GB。
    每个线程都会有自己的栈,但是堆空间是共用的。

  10. Text & Data & Bss

.text: 也称为代码段(Code),用来存放程序执行代码,同时也可能会包含一些常量(如一些字符串常量等)。该段内存为静态分配,只读(某些架构可能允许修改)。 这块内存是共享的,当有多个相同进程(Process)存在时,共用同一个text段。

.data: 也有的地方叫GVAR(global value),用来存放程序中已经初始化的非零全局变量。静态分配。 data又可分为读写(RW)区域和只读(RO)区域。
-> RO段保存常量所以也被称为.constdata
-> RW段则是普通非常全局变量,静态变量就在其中 .bss: 存放程序中为初始化的和零值全局变量。静态分配,在程序开始时通常会被清零。

接下来通过一张详细的图解来说明,具体的位置

在这里插入图片描述

三、在Ubuntu(x86)系统中输出信息进行验证

我们编写如下的代码,在Ubuntu中进行相应的验证

#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] = "yaoyao";//栈
    //定义常量字符串
    char *var1 = "1234567890";
    char *var2 = "abcdefghij";
    //动态分配——堆区
    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;
}


得到如下的结果:

在这里插入图片描述

分析结果

  1. 栈存放区域是由高地址到低地址向下增长

  2. 堆存放区是由低地址到高地址像上增长

  3. 静态变量地址从高地址到低地址向下增长

  4. 函数地址是从低地址到高地址向上增长

四、在Keil中针对stm32系统进行验证

​ 在Keil中编写如下代码:

#include "Serial.h"
#include <stdio.h>
#include <stdlib.h>
#include "stm32f10x.h"                  // Device header

//定义全局变量
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)
 {		
 	u16 t;  
	u16 len;	
	u16 times=0;
  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	Serial_Init();	
 	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] = "Lx567";
    //定义常量字符串
    char *var1 = "1234567890";
    char *var2 = "abcdefghij";
    //动态分配
    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;
	}	 
 }

编译成功
在这里插入图片描述

展示结果:

在这里插入图片描述

结果:stm32的栈区的地址值是从上到下减小的,堆区则是从上到下增长的

大佬友情链接

  • https://blog.csdn.net/qq_46467126/article/details/121875496
  • https://blog.csdn.net/qq_42762607/article/details/110391819
  • https://blog.csdn.net/He3he3he/article/details/84592000
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LX很爱吃葱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值