Chapter 4: Functions and Program Structure
4.1 Basic of Functions
A program is just a set of definitions of variables and functions. Communication between the functions is by arguments and values by the functions, and through external variables.
The return statement is the mechanism for returning a value from the called function to its caller. Any expression can follow return: return expression; The expression will be converted to the return type of the function if necessary. The calling function is free to ignore the returned value.
4.2 Functions Returning Non-integers
#include<ctype.h> /* atof: convert string 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; }
The function atof must be declared and defined consistently. If atof itself and the call to it in main have inconsistent types in the same source file, the error will be detected by the complier. But if (as is more likely) atof were complied separately, the mismatch would not be dtected, atof would return a double that main would treat as an int, and meaningless answers would result.
If the function takes arguments, declare them; if it takes no arguments, use void.
4.3 External Variables
External variables are defined outside of any function, and are thus potentially available to many functions. Functions themselves are always external, because C does not allow functions to be defined inside other functions. By default, external variables and functions have the property that all references to them by the same name, even from functions compiled separately, are references to the same thing.
4.4 Scope Rules
The scope of a name is the part of the program within which the name can be used. For an automatic variable declared at the beginning of a function, the scope is the function in which the name is declared. Local variables of the same name in different functions are unrelated. The same is true of the parameters of function, which are in effect local variables.
The scope of an external variable or a function lasts from the point at which it is declared to the end of the file being complied. There must be only one definition of an external variable among all the files that make up the source program; other files may contain extern declarations to access it.
A declaration announces the properties of a variable; a definition also causes storage to be set aside. Array sizes must be specified with the definition, but are optional with an extern declaration.
4.5 Header Files
We will place this common material in a header file, which will be included as necessary.
4.6 Static Variables
The static declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being complied. Normally, function names are global, visible to any part of the entire program. If a function is declared static, however, its name is invisible outside of the file in which it is declared. Internal static variables provide private, permanent storage within a single function.
4.7 Register Variables
A register declaration advises the complier that the variable in question will be heavily used. The idea is that register variables are to be placed in machine registers, which may result in smaller and faster programs. The register declaration can only be applied to automatic variables and to the formal parameters of a function.
It is not possible to take the address of a register variable. The specific restrictions on number and types of register variables vary from machine to machine.
4.8 Block Structure
C is not a block-structured language because functions may not be defined within other functions.
Declarations of variables (including initializations) may follow the left brace that introduces any compound statement. Variables declared in this way hide any identically named variables in outer blocks, and remain in existence until the matching right brace.
An automatic variable declared and initialized in a block is initialized each time the block is entered. A static variable is initialized only the first time the block is entered.
Automatic variables, including formal parameters, also hide external variables and functions of the same name.
4.9 Initialization
In the absence of explicit initialization, external and static variables are guaranteed to be initialized to zero; automatic and register variables have undefined (i.e. garbage) initial values.
For external and static variables, the initializer must be a constant expression; the initialization is done once, conceptually before the program begins execution.
For automatic and register variables, the intializer is not restricted to being a constant: it may be any expression involving previously defined values, even function calls; it is done each time the function or block is entered.
An array may be initialized by following its declaration with a list of initializers enclosed in braces and separated by commas. When the size of the array is omitted, the complier will compute the length by counting the initializers. If there are fewer initializers for an array than the number specified, the missing elements will be zero for external, static, and automatic variables. It is an error to have too many initializers.
Character arrays are a special case of initialization; a string may be instead of the braces and commas notation:
char pattern[] = "hello";
is a shorthand for the longer but equivalent:
char pattern[] = {'h', 'e', 'l', 'l', 'o', '\0'};
4.10 Recursion
C functions may be used recursively; that is a function may call itself either directly or indirectly.
#include<stdio.h> /* printd: print n in decimal */ void printd(int n) { if (n < 0) { putchar('-'); n = -n; } if (n / 10) printd(n / 10); putchar(n % 10 + '0'); }
/* qsort: sort v[left]...v[right] into increasing order */ void qsort(int v[], int left, int right) { int i, last; void swap(int v[], int i, int j); if (left >= right) /* do nothing if array contains fewer than two elements */ return; swap(v, left, (left + right) / 2); /* move partition elem to v[0] */ last = left; for (i = left + 1; i <= right; i++) /* partition */ if (v[i] < v[left]) swap(v, ++last, i); swap(v, left, right); /* restore partition elem */ qsort(v, left, last - 1); qsort(v, last + 1, right); } /* swap: interchange v[i] and v[j] */ void swap(int v[], int i, int j) { int temp; temp = v[i]; v[i] = v[j]; v[j] = temp; }
4.11 The C Preprocessor
Any source line of the form #include "filename" or #include <filename> is replaced by the contents of the file filename. If the filename is quoted, searching for the file typically begins where the source program was found; if it is not found there, or if the filename is enclosed in < and >, searching follows an implementation-defined rule to find the file. #include is preferred way to tie the declarations together for a large program. When an included file is changed, all files that depend on it must be recomplied.
A definition has the form #define name replacement-text. It calls for a macro substitution of the simplest kind-subsequent occurrences of the token name will be replaced by the replacement-text. A long definition may be continued onto several lines by placing a \ at the end of each line to be continued. The scope of a name defined with #define is from its point of definition to the end of the source file being complied. A definition may use previous definitions. Substitutions are made only for tokens, and do not take place within quoted strings. It is also possible to define macros with arguments.
#define max(A, B) ((A) > (B) ? (A) : (B))
One practical example comes from <stdio.h>, in which getchar and putchar are often defined as macros to avoid the run-time overhead of a function call per character processed. The functions in <ctype.h> are also usually implemented as macros.
Formal parameters are not replaced within quoted strings. If, however, a parameter name is preceded by a # in the replacement text, the combination will be expanded into a quoted string with the parameter replaced by the actual argument.
#define dprint(expr) printf(#expr " = %g\n", expr)
When this is invoked, as in dprint(x/y); the macro is expanded into
printf("x/y" " = %g\n", x/y);
and the strings are concatenated, so the effect is
printf("x/y = %g\n", x/y);
The preprocessor operator ## provides a way to concatenate actual arguments during macro expansion. If a parameter in the replacement text is adjacent to a ##, the parameter is replaced by the actual argument, the ## and surrounding white space are removed, and the result is re-scanned. For example, the macro paste concatenates its two arguments:
#define paste(front, back) front ## back
so paste(name, 1) creates the token name1.
The #if line evaluates a constant integer expression (which may not include sizeof, casts, or enum constants). If the expression is non-zero, subsequent lines until an #endif or #elif or #else are included. The expression defined(name) in a #if is 1 if the name has been defined, and 0 otherwise. The #ifdef and #ifndef lines are speciallized forms that test whether a name is defined.

浙公网安备 33010602011771号