单片机 局部变量存储在R0-R7,简单的LED点亮试验,观察反汇编就课看出,但是C51只允许通过工作寄存器传递三个参数,其他参数通过固定存储区传递
局部变量空间,就是堆栈空间,也就是栈空间。
从局部变量声明的时候,它就在堆栈空间了,而不是调用函数的时候,才让它入栈的。
定义一个局部变量a,编译器会将a的地址分配到寄存器组R0~R7中去。由于它是局部变量,所以编译器将使用立即数赋值语句为代表a的寄存器Rn赋值,最后计算的结果也将存在寄存器组中,位置由编译器任意指定。
定义一个全局变量a,编译器将在RAM中为变量a指定一个专用地址,在C程序中给a赋的值将存入这个专用地址中。程序操作变量a时,首先从专用地址中取出存放的值,然后再进行计算。
结论:
局部变量由于用寄存器直接操作,存取速度和计算速度都很快;由于寄存器数量有限,如果局部变量过多,将使代码由于频繁分配寄存器而变得冗长。
全局变量被定义在内存中的专门地址上,存取位置固定。对于频繁存取的重要变量可以采用全局变量以减少代码的长度;由于全局变量总是占用内存,如果过多,或者把程序处理和计算中的一些中间变量也定义成全局变量,将大量消耗内存空间,处理速度会减慢,同时数据安全性也会降低。
接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据。那么这些变量在内存中是如何存放的呢?程序又是如何使用这些变量的呢?下面就会对此进行深入的讨论。下文中的C语言代码如没有特别声明,默认都使用VC编译的release版。
首先,来了解一下 C 语言的变量是如何在内存分部的。C 语言有全局变量(Global)、本地变量(Local),静态变量(Static)、寄存器变量(Regeister)。每种变量都有不同的分配方式。先来看下面这段代码:
include
int g1=0, g2=0, g3=0;
int main()
{
static int s1=0, s2=0, s3=0;
int v1=0, v2=0, v3=0;
//打印出各个变量的内存地址
printf(“0xx\n”,&v1); //打印各本地变量的内存地址
printf(“0xx\n”,&v2);
printf(“0xx\n\n”,&v3);
printf(“0xx\n”,&g1); //打印各全局变量的内存地址
printf(“0xx\n”,&g2);
printf(“0xx\n\n”,&g3);
printf(“0xx\n”,&s1); //打印各静态变量的内存地址
printf(“0xx\n”,&s2);
printf(“0xx\n\n”,&s3);
return 0;
}
编译后的执行结果是:
0x0012ff78
0x0012ff7c
0x0012ff80
0x004068d0
0x004068d4
0x004068d8
0x004068dc
0x004068e0
0x004068e4
输 出的结果就是变量的内存地址。其中v1,v2,v3是本地变量,g1,g2,g3是全局变量,s1,s2,s3是静态变量。你可以看到这些变量在内存是连 续分布的,但是本地变量和全局变量分配的内存地址差了十万八千里,而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全局/静态变量是分配在不 同类型的内存区域中的结果。对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈 (stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然 代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数 据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。
补充:怎么让程序的全局变量不会漫天飞
1.使用结构体分类
2.把全局变量放在一个.c里面
3.如果外部函数要调用的话,就使用相应API(如cmSet_xxxxx() cmGet_xxxxx())
源程序
/* 名称:闪烁的LED
说明:LED按设定的时间间隔闪烁
*/
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit LED=P1^0;
//延时
void DelayMS(uint x)
{
uchar i;
while(x--)
{
for(i=0;i<120;i++);
}
}
//主程序
void main()
{
while(1)
{
LED=~LED;
DelayMS(150);
}
}
反汇编
126: ?C_STARTUP: LJMP STARTUP1
127:
128: RSEG ?C_C51STARTUP
129:
130: STARTUP1:
131:
132: IF IDATALEN <> 0
C:0x0000 020019 LJMP STARTUP1(C:0019)
9: void DelayMS(uint x)
10: {
11: uchar i;
12: while(x--)
C:0x0003 EF MOV A,R7
C:0x0004 1F DEC R7
C:0x0005 AA06 MOV R2,0x06
C:0x0007 7001 JNZ C:000A
C:0x0009 1E DEC R6
C:0x000A 4A ORL A,R2
C:0x000B 600B JZ C:0018
13: {
14: for(i=0;i<120;i++);
C:0x000D E4 CLR A
C:0x000E FD MOV R5,A
C:0x000F ED MOV A,R5
C:0x0010 C3 CLR C
C:0x0011 9478 SUBB A,#0x78
C:0x0013 50EE JNC DelayMS(C:0003)
C:0x0015 0D INC R5
C:0x0016 80F7 SJMP C:000F
15: }
16: }
17: //主程序
C:0x0018 22 RET
133: MOV R0,#IDATALEN - 1
C:0x0019 787F MOV R0,#0x7F
134: CLR A
C:0x001B E4 CLR A
135: IDATALOOP: MOV @R0,A
C:0x001C F6 MOV @R0,A
136: DJNZ R0,IDATALOOP
C:0x001D D8FD DJNZ R0,IDATALOOP(C:001C)
185: MOV SP,#?STACK-1
186:
187: ; This code is required if you use L51_BANK.A51 with Banking Mode 4
188: ;<h> Code Banking
189: ; <q> Select Bank 0 for L51_BANK.A51 Mode 4
190: #if 0
191: ; <i> Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.
192: EXTRN CODE (?B_SWITCH0)
193: CALL ?B_SWITCH0 ; init bank mechanism to code bank 0
194: #endif
195: ;</h>
C:0x001F 758107 MOV SP(0x81),#0x07
196: LJMP ?C_START
C:0x0022 020025 LJMP main(C:0025)
18: void main()
19: {
20: while(1)
21: {
22: LED=~LED;
C:0x0025 B290 CPL LED(0x90.0)
23: DelayMS(150);
C:0x0027 7F96 MOV R7,#0x96
C:0x0029 7E00 MOV R6,#0x00
C:0x002B 120003 LCALL DelayMS(C:0003)
24: }
C:0x002E 80F5 SJMP main(C:0025)
C:0x0030 00 NOP
C:0x0031 00 NOP