📄 chapter4.html
字号:
<html><head><title>Chapter 4 - Functions and Program Structure</title></head><body><hr><p align="center"><a href="chapter3.html">Back to Chapter 3</a> -- <a href="kandr.html">Index</a> -- <a href="chapter5.html">Chapter 5</a><p><hr><h1>Chapter 4 - Functions and Program Structure</h1>Functions break large computing tasks into smaller ones, and enable people tobuild on what others have done instead of starting over from scratch.Appropriate functions hide details of operation from parts of the program thatdon't need to know about them, thus clarifying the whole, and easing the painof making changes.<p>C has been designed to make functions efficient and easy to use; C programsgenerally consist of many small functions rather than a few big ones. A programmay reside in one or more source files. Source files may be compiled separatelyand loaded together, along with previously compiled functions from libraries.We will not go into that process here, however, since the details vary fromsystem to system.<p>Function declaration and definition is the area where the ANSI standard hasmade the most changes to C. As we saw first in <a href="chapter1.html">Chapter 1</a>, it isnow possible to declare the type of arguments when a function is declared. Thesyntax of function declaration also changes, so that declarations anddefinitions match. This makes it possible for a compiler to detect many moreerrors than it could before. Furthermore, when arguments are properly declared,appropriate type coercions are performed automatically.<p>The standard clarifies the rules on the scope of names; in particular, itrequires that there be only one definition of each external object.Initialization is more general: automatic arrays and structures may now beinitialized.<p>The C preprocessor has also been enhanced. New preprocessor facilities includea more complete set of conditional compilation directives, a way to createquoted strings from macro arguments, and better control over the macroexpansion process.<h2><a name="s4.1">4.1 Basics of Functions</a></h2>To begin with, let us design and write a program to print each line of itsinput that contains a particular ``pattern'' or string of characters. (This isa special case of the UNIX program <tt>grep</tt>.) For example, searching for thepattern of letters ``<tt>ould</tt>'' in the set of lines<pre> Ah Love! could you and I with Fate conspire To grasp this sorry Scheme of Things entire, Would not we shatter it to bits -- and then Re-mould it nearer to the Heart's Desire!</pre>will produce the output<pre> Ah Love! could you and I with Fate conspire Would not we shatter it to bits -- and then Re-mould it nearer to the Heart's Desire!</pre>The job falls neatly into three pieces:<pre>while (<em>there's another line</em>) if (<em>the line contains the pattern</em>) <em>print it</em></pre>Although it's certainly possible to put the code for all of this in <tt>main</tt>,a better way is to use the structure to advantage by making each part aseparate function. Three small pieces are better to deal with than one bigone, because irrelevant details can be buried in the functions, and thechance of unwanted interactions is minimized. And the pieces may even beuseful in other programs.<p>``While there's another line'' is <tt>getline</tt>, a function that we wrote in<a href="chapter1.html">Chapter 1</a>, and ``print it'' is <tt>printf</tt>, which someone hasalready provided for us. This means we need only write a routine to decidewhether the line contains an occurrence of the pattern.<p>We can solve that problem by writing a function <tt>strindex(s,t)</tt> thatreturns the position or index in the string <tt>s</tt> where the string <tt>t</tt>begins, or <tt>-1</tt> if <tt>s</tt> does not contain <tt>t</tt>. Because C arraysbegin at position zero, indexes will be zero or positive, and so a negativevalue like <tt>-1</tt> is convenient for signaling failure. When we later needmore sophisticated pattern matching, we only have to replace <tt>strindex</tt>;the rest of the code can remain the same. (The standard library provides afunction <tt>strstr</tt> that is similar to <tt>strindex</tt>, except that itreturns a pointer instead of an index.)<p>Given this much design, filling in the details of the program isstraightforward. Here is the whole thing, so you can see how the pieces fittogether. For now, the pattern to be searched for is a literal string, which isnot the most general of mechanisms. We will return shortly to a discussionof how to initialize character arrays, and in <a href="chapter5.html">Chapter 5</a>will show how to make the pattern a parameter that is set when the program is run.There is also a slightly different version of <tt>getline</tt>; you might find itinstructive to compare it to the one in <a href="chapter1.html">Chapter 1</a>.<pre> #include <stdio.h> #define MAXLINE 1000 /* maximum input line length */ int getline(char line[], int max) int strindex(char source[], char searchfor[]); char pattern[] = "ould"; /* pattern to search for */ /* find all lines matching pattern */ main() { char line[MAXLINE]; int found = 0; while (getline(line, MAXLINE) > 0) if (strindex(line, pattern) >= 0) { printf("%s", line); found++; } return found; } /* getline: get line into s, return length */ int getline(char s[], int lim) { int c, i; i = 0; while (--lim > 0 && (c=getchar()) != EOF && c != '\n') s[i++] = c; if (c == '\n') s[i++] = c; s[i] = '\0'; return i; } /* strindex: return index of t in s, -1 if none */ int strindex(char s[], char t[]) { int i, j, k; for (i = 0; s[i] != '\0'; i++) { for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++) ; if (k > 0 && t[k] == '\0') return i; } return -1; }</pre>Each function definition has the form<pre><em>return-type function-name</em>(<em>argument declarations</em>){ <em>declarations and statements</em>}</pre>Various parts may be absent; a minimal function is<pre> dummy() {}</pre>which does nothing and returns nothing. A do-nothing function like this issometimes useful as a place holder during program development. If the returntype is omitted, <tt>int</tt> is assumed.<p>A program is just a set of definitions of variables and functions.Communication between the functions is by arguments and values returned bythe functions, and through external variables. The functions can occur inany order in the source file, and the source program can be split intomultiple files, so long as no function is split.<p>The <tt>return</tt> statement is the mechanism for returning a value from thecalled function to its caller. Any expression can follow <tt>return</tt>:<pre> return <em>expression</em>;</pre>The <em>expression</em> will be converted to the return type of the functionif necessary. Parentheses are often used around the <em>expression</em>, butthey are optional.<p>The calling function is free to ignore the returned value. Furthermore, thereneed to be no expression after <tt>return</tt>; in that case, no value is returnedto the caller. Control also returns to the caller with no value when execution``falls off the end'' of the function by reaching the closing right brace. Itis not illegal, but probably a sign of trouble, if a function returns a valuefrom one place and no value from another. In any case, if a function fails toreturn a value, its ``value'' is certain to be garbage.<p>The pattern-searching program returns a status from <tt>main</tt>, the number ofmatches found. This value is available for use by the environment that calledthe program<p>The mechanics of how to compile and load a C program that resides on multiplesource files vary from one system to the next. On the UNIX system, forexample, the <tt>cc</tt> command mentioned in <a href="chapter1.html">Chapter 1</a>does the job. Suppose that the three functions are stored in three filescalled <tt>main.c</tt>, <tt>getline.c</tt>, and <tt>strindex.c</tt>. Thenthe command<pre> cc main.c getline.c strindex.c</pre>compiles the three files, placing the resulting object code in files<tt>main.o</tt>, <tt>getline.o</tt>, and<tt>strindex.o</tt>, then loads them all into an executable file called<tt>a.out</tt>. If there is an error, say in <tt>main.c</tt>, the file can berecompiled by itself and the result loaded with the previous object files,with the command<pre> cc main.c getline.o strindex.o</pre>The <tt>cc</tt> command uses the ``<tt>.c</tt>'' versus ``<tt>.o</tt>'' namingconvention to distinguish source files from object files.<p><strong>Exercise 4-1.</strong> Write the function <tt>strindex(s,t)</tt> which returnsthe position of the <em>rightmost</em> occurrence of <tt>t</tt> in <tt>s</tt>, or<tt>-1</tt> if there is none.<h2><a name="s4.2">4.2 Functions Returning Non-integers</a></h2>So far our examples of functions have returned either no value (<tt>void</tt>)or an <tt>int</tt>. What if a function must return some other type? manynumerical functions like <tt>sqrt</tt>, <tt>sin</tt>, and <tt>cos</tt> return<tt>double</tt>; other specialized functions return other types. To illustratehow to deal with this, let us write and use the function <tt>atof(s)</tt>, whichconverts the string <tt>s</tt> to its double-precision floating-point equivalent.<tt>atof</tt> if an extension of <tt>atoi</tt>, which we showed versions ofin <a href="chapter2.html">Chapters 2</a> and <a href="chapter3.html">3</a>.It handles an optional sign and decimal point, and the presence or absence ofeither part or fractional part. Our version is <em>not</em> a high-qualityinput conversion routine; that would take more space than we care to use. Thestandard library includes an <tt>atof</tt>; the header <tt><stdlib.h></tt>declares it.<p>First, <tt>atof</tt> itself must declare the type of value it returns, sinceit is not <tt>int</tt>. The type name precedes the function name:<pre> #include <ctype.h> /* atof: convert string s to double */ double atof(char s[]) { double val, power; int i, sign; for (i = 0; isspace(s[i]); i++) /* skip white space */ ; sign = (s[i] == '-') ? -1 : 1; if (s[i] == '+' || s[i] == '-') i++; for (val = 0.0; isdigit(s[i]); i++) val = 10.0 * val + (s[i] - '0'); if (s[i] == '.') i++; for (power = 1.0; isdigit(s[i]); i++) { val = 10.0 * val + (s[i] - '0'); power *= 10; } return sign * val / power; }</pre>Second, and just as important, the calling routine must know that <tt>atof</tt>returns a non-int value. One way to ensure this is to declare <tt>atof</tt>explicitly in the calling routine. The declaration is shown in this primitivecalculator (barely adequate for check-book balancing), which reads one numberper line, optionally preceded with a sign, and adds them up, printing therunning sum after each input:<pre> #include <stdio.h> #define MAXLINE 100 /* rudimentary calculator */ main() { double sum, atof(char []); char line[MAXLINE]; int getline(char line[], int max); sum = 0; while (getline(line, MAXLINE) > 0) printf("\t%g\n", sum += atof(line)); return 0; }</pre>The declaration<pre> double sum, atof(char []);</pre>says that <tt>sum</tt> is a <tt>double</tt> variable, and that <tt>atof</tt> is afunction that takes one <tt>char[]</tt> argument and returns a <tt>double</tt>.<p>The function <tt>atof</tt> must be declared and defined consistently. If<tt>atof</tt> itself and the call to it in <tt>main</tt> have inconsistent types in thesame source file, the error will be detected by the compiler. But if (as ismore likely) <tt>atof</tt> were compiled separately, the mismatch would not bedetected, <tt>atof</tt> would return a <tt>double</tt> that <tt>main</tt> wouldtreat as an <tt>int</tt>, and meaningless answers would result.<p>In the light of what we have said about how declarations must matchdefinitions, this might seem surprising. The reason a mismatch can happen isthat if there is no function prototype, a function is implicitly declared byits first appearance in an expression, such as<pre> sum += atof(line)</pre>If a name that has not been previously declared occurs in an expression and isfollowed by a left parentheses, it is declared by context to be a functionname, the function is assumed to return an <tt>int</tt>, and nothing is assumedabout its arguments. Furthermore, if a function declaration does not includearguments, as in<pre> double atof();</pre>that too is taken to mean that nothing is to be assumed about the argumentsof <tt>atof</tt>; all parameter checking is turned off. This special meaningof the empty argument list is intended to permit older C programs to compilewith new compilers. But it's a bad idea to use it with new C programs. If thefunction takes arguments, declare them; if it takes no arguments, use<tt>void</tt>.<p>Given <tt>atof</tt>, properly declared, we could write <tt>atoi</tt> (convert astring to <tt>int</tt>) in terms of it:<pre> /* atoi: convert string s to integer using atof */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -