📄 chapter 4 variables and constants -- valvano.htm
字号:
unspecified memory (stack) locations, then how does the program find them? This
is done by using the pointer register (X) to designate a stack frame for the
currently active function. There is a difference between the ICC11 and ICC12
compilers. The ICC11 compiler generates code that will define a new value of X
(executing the <B>tsx</B> instruction) whenever it wishes to access a local
variable. Consequently we see many <B>tsx</B> instructions throughout the
function. On the other hand, the ICC12 compiler generates code that attempts to
define the stack frame pointer x only once at the beginning of the function.
Consequently we usually see only one <B>tfr s,x</B> instruction in each the
function. The 6812 <B>tfr s,x</B> instruction is just an alternative
specification of the instruction <B>tsx</B> (i.e., they produce the same machine
code and perform the same function when executed). When the ICC12 function is
entered, the prior value of Register X is pushed onto the stack and then the new
value of SP is moved to X. The ICC11 function does not save the prior value of
Register X. This address--the new value of SP--then becomes the base for
references to local variables that are declared within the function. The 6812
has a much richer set of machine instructions and addressing modes to simplify
this process. The 6811 SP register points to a free memory space to be used to
place the next byte to be pushed. On the other hand the 6812 SP register points
to the top data byte that has already been pushed.</FONT></P>
<P><FONT face="Times New Roman,Times">In order to understand both the machine
architecture and the C compiler, we can look at the assembly code generated. For
both the ICC11 and ICC12 compilers, the linker/loader allocates 3 segmented
memory areas: code pointed to by the PC (<I>text area</I>); global accessed with
absolute addressing (<I>data area</I>); and locals pointed to by the stack
pointer SP. This example shows a simple C program with three local variables.
Although the function doesn't do much it will serve to illustrate how local
variables are created (allocation), accessed (read and write) and destroyed
(deallocated.)</FONT></P>
<DIR>
<P><CODE>void sub(void){ short y1,y2,y3; /* 3 local
variables*/<BR> y1=1000;<BR> y2=2000;<BR> y3=y1+y2;<BR>}</CODE></P></DIR>
<ADDRESS><FONT face="Times New Roman,Times">Listing 4-9: Example showing three
local variables</FONT></ADDRESS>
<P><FONT face="Times New Roman,Times">The first compiler we will study is the
ImageCraft ICC11 version 4.0 for the Motorola 6811. The disassembled output has
been edited to clarify its operation (although the compiler does create the
"; y3 -> 0,x" comment). The <B>pshx</B> instruction allocates
the local variable, and the <B>tsx</B> instruction establishes a stack frame
pointer, X. </FONT></P>
<DIR>
<P><CODE> .area text ; _sub in
ROM<BR> .globl _main<BR>; y3 -> 0,x<BR>; y2 -> 2,x<BR>; y1 -> 4,x<BR>_sub: pshx ;
allocate y1<BR> pshx ; allocate
y2<BR> pshx ; allocate
y3<BR> tsx<BR> ldd #1000 <BR> std 4,x ; y1=1000<BR> ldd #2000<BR> std 2,x ; y2=2000<BR> ldd 4,x<BR> addd 2,x<BR> std 0,x ; y3=y1+y2<BR> pulx ;
deallocate
y3<BR> pulx ;
deallocate
y2<BR> pulx ;
deallocate y1<BR> rts</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">The stack frame at the time of the
<B>addd</B> instruction is shown. Within the subroutine the local variables of
other functions are not accessible.</FONT></P>
<CENTER>
<P><IMG height=103
src="Chapter 4 Variables and Constants -- Valvano.files/stack11a.gif"
width=218></P></CENTER>
<ADDRESS>Figure 4-1. 6811 stack frame showing three local variables.</ADDRESS>
<P><FONT face="Times New Roman,Times">The next compiler we will study is the
ImageCraft ICC12 version 5.0 for the Motorola 6812. Again, the disassembled
output has been edited to clarify its operation (although the compiler does
create the "</FONT><FONT face=Courier>; y3 -> -6,x</FONT><FONT
face="Times New Roman,Times">" comment). Like the 6811, the linker/loader also
allocates 3 segmented memory areas: code pointed to by the PC; global accessed
with absolute addressing; and locals pointed to by the stack pointer SP.
The</FONT><FONT face=Courier> <B>leas -6,sp</B></FONT><FONT
face="Times New Roman,Times"> instruction allocates the local variables, and
the</FONT><B><FONT face=Courier> tfr s,x</FONT></B><FONT face=Courier>
</FONT><FONT face="Times New Roman,Times">instruction establishes a stack frame
pointer, X. Within the subroutine the local variables of other functions are not
accessible.</FONT></P>
<CENTER>
<P><IMG height=322
src="Chapter 4 Variables and Constants -- Valvano.files/lv.gif"
width=370> </P></CENTER>
<ADDRESS>Figure 4-2. 6812 implementation of three local variables.</ADDRESS>
<P> </P>
<P><FONT face="Times New Roman,Times"><A name=CONSTANTLOCAL></A>A <B>constant
local</B> is similar to the regular local. Just as with the other locals, the
constant is defined temporarily on the stack. The difference is that the
constant local can not be changed. The assembly language code generated by the
compiler that accesses the constant local is identical to the regular local.
</FONT></P>
<DIR>
<P><CODE>short TheGlobal; /* a regular global
variable*/<BR>void main(void){ <BR> const short
TheConstant=1000; /* a constant
local*/<BR> TheGlobal=TheConstant;<BR>}</CODE></P></DIR>
<ADDRESS>Listing 4-10: Example showing a constant local </ADDRESS>
<P><FONT face="Times New Roman,Times">The 6811 code generated by the ICC11
(Version 4) compiler is as follows</FONT></P>
<DIR>
<P><CODE> .area text ; _main in
ROM<BR> .global
_main<BR>; TheConstant -> 0,x<BR>_main: pshx ;
allocate
TheConstant<BR> tsx<BR> ldd #1000<BR> std 0,x ; TheConstant=1000<BR> std _TheGlobal ; TheGlobal=TheConstant<BR> pulx ;
deallocate
TheConstant<BR> rts<BR> .area
bss<BR> .global _TheGlobal<BR>_TheGlobal: .blkb 2
</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">The 6812 code generated by the ICC12
(Version 5.1) compiler is as follows</FONT></P>
<DIR>
<P><CODE> .area text <BR>_main::
<BR> pshx<BR> tfr
s,x<BR> leas -2,sp <BR> movw
#1000,-2,x <BR> movw -2,x,_TheGlobal
<BR> tfr
x,s<BR> pulx<BR> rts
<BR> .area bss <BR>_TheGlobal:: .blkb 2 </CODE></P>
<P> </P></DIR>
<P><B><I><FONT face=Helvetica,Arial><A
name=EXTERNAL></A>Externals</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">Objects that are defined outside of the
present source module have the external storage class. This means that, although
the compiler knows what they are (signed/unsigned, 8-bit 16-bit 32-bit etc.), it
has no idea where they are. It simply refers to them by name without reserving
space for them. Then when the linker brings together the object modules, it
resolves these "pending" references by finding the external objects and
inserting their addresses into the instructions that refer to them. The compiler
knows an external variable by the keyword <B>extern</B> that must precede its
declaration.</FONT></P>
<P><FONT face="Times New Roman,Times">Only global declarations can be designated
extern and only globals in other modules can be referenced as
external.</FONT></P>
<P><FONT face="Times New Roman,Times">The following example sets an external
global, called <B>ExtGlobal</B>, to the value 1000. This global can be
referenced by any function from any file in the software system. It is truly
global.</FONT></P>
<DIR>
<P><CODE>extern short ExtGlobal; /* an external global
variable*/<BR>void main(void){
<BR> ExtGlobal=1000; <BR>}</CODE></P></DIR>
<ADDRESS>Listing 4-11: Example showing an external global</ADDRESS>
<P><FONT face="Times New Roman,Times">Notice the assembly language the ICC11
generates does not include the definition of <B>ExtGlobal</B>. The 6811 code
generated by the ICC11 (Version 4) compiler is as follows</FONT></P>
<DIR>
<P><CODE> .area text <BR> .global
_main<BR>_main:<BR> ldd #1000
<BR> std _ExtGlobal <BR> rts
</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">Similarly the assembly language the ICC12
generates also does not include the definition of <B>ExtGlobal</B>. The 6812
code generated by the ICC12 (Version 5.1) compiler is as follows</FONT></P>
<DIR>
<P><CODE> .area text <BR>_main::
<BR> movw #1000,_ExtGlobal
<BR> rts </CODE></P>
<P> </P></DIR>
<P><B><I><FONT face=Helvetica,Arial><A name=SCOPE></A>Scope</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">The <I>scope</I> of a variable is the
portion of the program from which it can be referenced. We might say that a
variable's scope is the part of the program that "knows" or "sees" the variable.
As we shall see, different rules determine the scopes of global and local
objects.</FONT></P>
<P><FONT face="Times New Roman,Times">When a variable is declared globally
(outside of a function) its scope is the part of the source file that follows
the declaration--any function following the declaration can refer to it.
Functions that precede the declaration cannot refer to it. Most C compilers
would issue an error message in that case. </FONT></P>
<P><FONT face="Times New Roman,Times">The scope of local variables is the block
in which they are declared. Local declarations must be grouped together before
the first executable statement in the block--at the head of the block. This is
different from C++ that allows local variables to be declared anywhere in the
function. It follows that the scope of a local variable effectively includes all
of the block in which it is declared. Since blocks can be nested, it also
follows that local variables are seen in all blocks that are contained in the
one that declares the variables.</FONT></P>
<P><FONT face="Times New Roman,Times">If we declare a local variable with the
same name as a global object or another local in a superior block, the new
variable temporarily supersedes the higher level declarations. Consider the
following program.</FONT></P>
<DIR>
<P><CODE>unsigned char x; /* a regular global
variable*/<BR>void
sub(void){<BR> x=1;<BR> {
unsigned char x; /* a local
variable*/<BR> x=2;<BR> { unsigned
char x; /* a local
variable*/<BR> x=3;<BR> PORTA=x;}<BR> PORTA=x;}<BR> PORTA=x;}<BR>}</CODE></P></DIR>
<ADDRESS>Listing 4-12: An example showing the scope of local variables</ADDRESS>
<P><FONT face="Times New Roman,Times">This program declares variables with the
name <B>x</B>, assigns values to them, and outputs them to PORTA with in such a
way that, when we consider its output, the scope of its declarations becomes
clear. When this program runs, it outputs 321. This only makes sense if the
<B>x</B>declared in the inner most block masks the higher level declarations so
that it receives the value '3' without destroying the higher level variables.
Likewise the second <B>x</B>is assigned '2' which it retains throughout the
execution of the inner most block. Finally, the global <B>x</B>, which is
assigned '1', is not affected by the execution of the two inner blocks. Notice,
too, that the placement of the last two <B>PORTA=x;</B> statements demonstrates
that leaving a block effectively unmasks objects that were hidden by
declarations in the block. The second<B> PORTA=x; </B>sees the middle
<B>x</B>and the last <B>PORTA=x;</B> sees the global <B>x</B>.</FONT></P>
<P><FONT face="Times New Roman,Times">This masking of higher level declarations
is an advantage, since it allows the programmer to declare local variables for
temporary use without regard for other uses of the same names.</FONT></P>
<P><FONT face="Times New Roman,Times">One of the mistakes a C++ programmer makes
when writing C code is trying to define local variables in the middle of a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -