📄 chapter 10 functions -- valvano.htm
字号:
them. In C, parentheses following a name are associated with the name before the
preceding asterisk is applied to the result. Therefore,</FONT></P>
<DIR>
<P><CODE>int *fp(int);</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">would be taken as</FONT></P>
<DIR>
<P><CODE>int *(fp(int));</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">saying that <B>fp</B> is a function
returning a pointer to an integer, which is not at all like the declaration in
Listing 10-1.</FONT></P>
<P> </P>
<P><B><I><FONT face=Helvetica,Arial><A name=DEFINITIONS></A>Function
Definitions</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">The second way to declare a function is to
fully describe it; that is, to <I>define</I> it. Obviously every function must
be defined somewhere. So if we organize our source code in a bottom up fashion,
we would place the lowest level functions first, followed by the function that
calls these low level functions. It is possible to define large project in C
without ever using a standard declaration (function prototype). On the other
hand, most programmers like the top-down approach illustrated in the following
example. This example includes three modules: the LCD interface, the COP
functions, and some Timer routines. Notice the function names are chosen to
reflect the module in which they are defined. If you are a C++ programmer,
consider the similarities between this C function call <B>LCDclear()</B> and a
C++ LCD class and a call to a member function <B>LCD.clear()</B>. The *.H files
contain function declarations and the *.C files contain the
implementations.</FONT></P>
<DIR>
<P><CODE>#include "HC12.H"<BR>#include "LCD12.H"<BR>#include
"COP12.H"<BR>#include "Timer.H"<BR>void main(void){ char letter; int n=0;
<BR> COPinit(); // Enable TOF interrupt to make COP
happy<BR> LCDinit();<BR> TimerInit()<BR> LCDString("Adapt812
LCD");<BR> TimerMsWait(1000);<BR> LCDclear();<BR> letter='a'-1;<BR> while(1){<BR> if
(letter=='z')<BR> letter='a';<BR> else<BR> letter++;<BR> LCDputchar(letter);<BR> TimerMsWait(250);<BR> if(++n==16){<BR> n=0;<BR> LCDclear();<BR> }<BR> }<BR>}<BR>#include
"LCD12.C"<BR>#include "COP12.C"<BR>#include "Timer.C"<BR>#include
"VECTORS.C"</CODE></P></DIR>
<ADDRESS>Listing 10-2: Modular approach to software development</ADDRESS>
<ADDRESS> </ADDRESS>
<P><FONT face="Times New Roman,Times">C function definitions have the following
form</FONT></P>
<DIR>
<P><I><FONT face="Times New Roman,Times">type Name</FONT></I><FONT
face="Times New Roman,Times">(<I>parameter
list</I>){<BR><I>CompoundStatement</I><BR><I>};</I></FONT></P></DIR>
<P><FONT face="Times New Roman,Times">Just like the function declaration, we
begin the definition with its <B>type</B>. The <B>type</B> specifies the
function return parameter. If there is no return parameter we can use
<B>void</B> or leave it blank. <B>Name</B> is the name of the function. The
<B>parameter list </B>is a list of zero or more names for the arguments that
will be received by the function when it is called. Both the type and name of
each input parameter is required. As we will see later, ICC11 and ICC12 pass the
first (left most) parameter in Reg D, and the remaining parameters are passed on
the stack. Then once inside the function, ICC12 and ICC12 functions will push
register D on the stack, so after that all parameters are on the stack. The
output parameter is returned in register D. 8-bit output parameters are promoted
to 16-bits. Similarly, most input parameters are also passed as 16-bit values,
8-bit characters are promoted to 16-bit integers and arrays and strings are
passed as pointers. The exception to this rule is 32-bit longs and 32-bit
floats.</FONT></P>
<P><FONT face="Times New Roman,Times">Although a character is passed as a word,
we are free to declare its formal argument as either character or word. If it is
declared as a character, only the low-order byte of the actual argument will be
referenced. If it is declared as an integer, then all 16 bits will be
referenced. </FONT></P>
<P><FONT face="Times New Roman,Times">It is generally more efficient to
reference integers than characters because there is no need for a machine
instruction to set the high-order byte. So it is common to see situations in
which a character is passed to a function which declares the argument to be an
integer. But there is one caveat here: not all C compilers promote character
arguments to integers when passing them to functions; the result is an
unpredictable value in the high-order byte of the argument. This should be
remembered as a portability issue.</FONT></P>
<P><FONT face="Times New Roman,Times">Since there is no way in C to declare
strings, we cannot declare formal arguments as strings, but we can declare them
as character pointers or arrays. In fact, as we have seen, C does not recognize
strings, but arrays of characters. The string notation is merely a shorthand way
of writing a constant array of characters.</FONT></P>
<P><FONT face="Times New Roman,Times">Furthermore, since an unsubscripted array
name yields the array's address and since arguments are passed by value, an
array argument is effectively a pointer to the array. It follows that, the
formal argument declarations <B>arg[]</B> and <B>*arg</B> are really equivalent.
The compiler takes both as pointer declarations. Array dimensions in argument
declarations are ignored by the compiler since the function has no control over
the size of arrays whose addresses are passed to it. It must either assume an
array's size, receive its size as another argument, or obtain it
elsewhere.</FONT></P>
<P><FONT face="Times New Roman,Times">The last, and most important, part of the
function definition above is <B>CompoundStatement</B>. This is where the action
occurs. Since compound statements may contain local declarations, simple
statements, and other compound statements, it follows that functions may
implement algorithms of any complexity and may be written in a structured style.
Nesting of compound statements is permitted without limit.</FONT></P>
<P><FONT face="Times New Roman,Times">As an example of a function definition
consider</FONT></P>
<DIR>
<P><CODE>int add3(int z1, int z2, int z3){ int
y;<BR> y=z1+z2+z3;<BR> return(y);}</CODE></P></DIR>
<ADDRESS>Listing 10-3: Example function with 3 inputs and one output.</ADDRESS>
<ADDRESS> </ADDRESS>
<P><FONT face="Times New Roman,Times">Here is a function named <B>add3</B> which
takes three input arguments.</FONT></P>
<P> </P>
<P><B><I><FONT face=Helvetica,Arial><A name=CALLS></A>Function
Calls</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">A function is called by writing its name
followed by a parenthesized list of argument expressions. The general form
is</FONT></P>
<P><I><FONT face="Times New Roman,Times">Name</FONT></I><FONT
face="Times New Roman,Times"> <B>(</B><I>parameter list</I><B>)</B></FONT></P>
<P><FONT face="Times New Roman,Times">where <B>Name</B> is the name of the
function to be called. The<B><I> </I>parameter list<I> </I></B>specifies the
particular input parameters used in this call. Notice that each input parameter
is in fact an expression. It may be as simple as a variable name or a constant,
or it may be arbitrarily complex, including perhaps other function calls.
Whatever the case, the resulting value is pushed onto the stack where it is
passed to the called function.</FONT></P>
<P><FONT face="Times New Roman,Times">C programs evaluate arguments from left to
right, pushing them onto the stack in that order. As we will see later, the
ICC11 and ICC12 compilers allocate the stack space for the parameters at the
start of the program that will make the function call. Then the values are
stored into the pre-allocated stack position before it calls the function. On
return, the return parameter is located in Reg D. The input parameters are
removed from the stack at the end of the program.</FONT></P>
<P><FONT face="Times New Roman,Times">When the called function receives control,
it refers to the first actual argument using the name of the first formal
argument. The second formal argument refers to the second actual argument, and
so on. In other words, actual and formal arguments are matched by position in
their respective lists. Extreme care must be taken to ensure that these lists
have the same number and type of arguments. </FONT></P>
<P><FONT face="Times New Roman,Times">It was mentioned earlier, that function
calls appear in expressions. But, since expressions are legal statements, and
since expressions may consist of only a function call, it follows that a
function call may be written as a complete statement. Thus the
statement</FONT></P>
<DIR>
<P><CODE>add3(--counter,time+5,3);</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">is legal. It calls <B>add3()</B>, passing
it three arguments --<B>counter</B>, <B>time+5</B>, and <B>3</B>. Since this
call is not part of a larger expression, the value that <B>add3()</B> returns
will be ignored. As a better example, consider</FONT></P>
<DIR>
<P><CODE>y=add3(--counter,time+5,3);</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">which is also an expression. It calls
<B>add3()</B> with the same arguments as before but this time it assigns the
returned value to <B>y</B>. It is a mistake to use an assignment statement like
the above with a function that does not return an output parameter.</FONT></P>
<P><FONT face="Times New Roman,Times">The ability to pass one function a pointer
to another function is a very powerful feature of the C language. It enables a
function to call any of several other functions with the caller determining
which subordinate function is to be called. </FONT></P>
<DIR>
<P><CODE>int fun1(int
input){<BR> return(input+1); // this
adds 1<BR>};<BR>int fun2(int
input){<BR> return(input+2); // this
adds 2<BR>};<BR>int execute(int (*fp)(int)){ int
data;<BR> data=(*fp)(5); //
data=fun1(5);<BR> return (data);<BR>};<BR>void main(void){ int
result;<BR> result=execute(&fun1); //
result=fun1(5);<BR> result=execute(&fun2); //
result=fun2(5);<BR>};</CODE></P></DIR>
<ADDRESS>Listing 10-4: Example of passing a function pointer</ADDRESS>
<P><FONT face="Times New Roman,Times">Notice that <B>fp</B> is declared to be a
function pointer. Also, notice that the designated function is called by writing
an expression of the same form as the declaration. </FONT></P>
<P><B><I><FONT face=Helvetica,Arial><A name=ARGUMENTS></A>Argument
Passing</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">Now let us take a closer look at the
matter of argument passing. With respect to the method by which arguments are
passed, two types of subroutine calls are used in programming languages--<I>call
by reference</I> and <I>call by value</I>.</FONT></P>
<P><FONT face="Times New Roman,Times">The<I> call by reference</I> method passes
arguments in such a way that references to the formal arguments become, in
effect, references to the actual arguments. In other words, references
(pointers) to the actual arguments are passed, instead of copies of the actual
arguments themselves. In this scheme, assignment statements have implied side
effects on the actual arguments; that is, variables passed to a function are
affected by changes to the formal arguments. Sometimes side effects are
beneficial, and some times they are not. Since C supports only one formal output
parameter, we can implement additional output parameters using call by
reference. In this way the function can return parameters back using the
reference. As an example recall the fifo queue program shown earlier in<A
href="http://www.ece.utexas.edu/~valvano/embed/chap8/chap8.htm#FIFOQ"> Listing
8-7</A>. The function <B>GetFifo</B>, shown below, returns two parameters. The
regular formal parameter is a boolean specifying whether or not the request was
successful, and the actual data removed from the queue is returned via the call
by reference. The calling program <B>InChar</B> passes the address of its local
variable data. The assignment statement <B>*datapt=Fifo[GetI++];</B> within
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -