2. Ch9 Functions
9.1 Defining and Calling Functions
9.2 Function Declarations
9.3 Arguments
9.4 The return Statement
9.5 Program Termination
9.6 Recursion
3. Ch9 Functions
• In Chapter2, a function is simply a series of statements that have
been grouped together and given a name
• Although the term “function” comes from mathematics, C functions
don’t always resemble math functions
C functions don’t necessarily have arguments
C functions don’t necessarily compute a value
• Functions are the building blocks of C programs
We can divide a program into small pieces that are easier to understand and
modify using functions
• Functions are reusable
4. 9.1 Defining and Calling Functions: Computing
Averages
Return type parameters
body
arguments
5. Parameters? Arguments?
• There is no general agreement on terminology.
This book Other books
parameter
Formal argument
Formal parameter
argument
Actual argument
Actual parameter
8. 9.1 Defining and Calling Functions: Function
Definitions (1/3)
return-type function-name ( parameters )
{
declarations
statements
}
• Functions may not return arrays, but there are no other restrictions on the return type
• Specifying that the return type is void indicates that the function doesn’t return a value
• Functions omit the return type
1. In C89 it will return a value of type int
2. In C99 it is illegal
• Some programmers put the return type above the function name
Especially useful if the return type is lengthy
double
average (double a, double b)
{
return (a + b) / 2;
}
9. 9.1 Defining and Calling Functions: Function
Definitions (2/3)
return-type function-name ( parameters )
{
declarations
statements
}
• Each parameter is preceded by a specification of its type
A separate type must be specified for each parameter, even when several parameters have the same type
• Parameters are separated by commas
• The word void should appear between the parentheses if the function has no parameters
double average (double a, b) /* WRONG */
{
return (a + b) / 2;
}
10. 9.1 Defining and Calling Functions: Function
Definitions (3/3)
return-type function-name ( parameters )
{
declarations
statements
}
• The body of a function body may include both declarations and statements
• Variables declared in the body of a function belong exclusively to that function
1. In C89, variable declarations must come first, before all statements in the body of a function
2. In C99, variable declarations and statements can be mixed
• The body of a function whose type is void can be empty
double average (double a, double b)
{
double sum; /* declaration */
sum = a + b; /* statement */
return sum / 2; /* statement */
}
void average (double a, double b)
{
}
11. Nested Function Definitions
• Some programming languages allow procedures and functions to be
nested within each other
• C does not permit the definition of one function to appear in the body of
another
12. 9.1 Defining and Calling Functions: Function
Calls (1/2)
• A functions call consists of a function name followed by a
list of arguments enclosed in parentheses
If the parentheses are missing, the function won’t get called
− If it is a function with void parameter, it is a legal expression statement
but has no effect
− “statement with no effect”
• A call of a void function is always followed by a
semicolon to turn it into a statement
• A call of a non-valid function
average(x, y)
print_count( i )
print_pun( )
print_pun( );
/* WRONG*/
print_count( i );
print_pun( );
avg = average(x, y);
If (average(x, y) > 0)
printf(“Average is positiven”);
printf(“The average is %gn”, average(x, y));
Produces a value that can be stored in a variable,
tested, printed, or used in some other way
The value returned by a non-valid function can
always be discarded if it’s not needed average(x, y); /* discards return value */
13. Call Function Name without Parentheses Followed
• The compiler treats a function name not followed by parentheses
as a pointer to the function
The compiler can’t automatically assume that a function name without
parentheses is an error
Treats as an expression and makes this function a valid expression
statement
pointers to
functions
Ch17.7
expression
statements
Ch4.5
print_pun;
14. 9.1 Defining and Calling Functions: Function
Calls (2/2)
• Ignore the return value seems an odd thing to do
• But it makes sense for some functions
printf
− Return the number of characters that it prints
− We may normally discard printf’s return value since we are not interested in the number
of characters printed
C allows to put (void) before the call to make it clear that the return value of a
function is discarded
− Cast (Convert) the return value of the functions to type void
num_char = printf(“Hello World!n”);
printf(“Hello World!n”); /* discards return value */
(void) printf(“Hello World!n”);
15. Comma in the Function Call
• The arguments in a function call must be assignment expressions
Can’t contain commas used as operators unless they’re enclosed in parentheses
1. The comma is punctuation
2. The comma is an operator
f(a, b);
f( (a, b) );
16. 9.1 Defining and Calling Functions: Testing
Whether a Number Is Prime
17. 9.2 Function Declarations (1/2)
• The definition of function can be placed above the point at which it is called
• What if the function is called before its definition?
The compilers will assume that function returns an int value
− Implicit declaration
The compilers can’t check the passed arguments have the proper type
− It performs the default argument promotions
When the compilers encounter the definition of the function, and the function’s return
type is not the same as the default int type, there will be an error message
• Avoid the problem of call-before-definition
1. Arrange the program so that the definition of each function precedes all its calls
It may make the program harder to understand by putting its function definitions in an unnatural order
2. Declare each function before calling it
default
argument
promotions
Ch9.3
18. 9.2 Function Declarations (2/2)
• Function declaration
Provides the compiler with a brief glimpse at a function whose full definition will appear later
Resembles the first line of a function definition with a semicolon added at the end
Must be consistent with the function’s definition
• Function prototype
Provides a complete description of how to call a function
1. How many arguments to supply
2. What their types should be
3. What type of result will be return
Doesn’t have to specify the names of the function’s parameters
− It’s usually best not to omit parameter names
Document the purpose of each parameter
Remind the programmer of the order in which arguments must appear when the function is called
− There are legitimate reasons for omitting parameter names, and some programmers prefer to do so
− The parameter names don’t have to match the names given later in the function’s definition
• C99 has adopted the rule that either a declaration or a definition of a function must be
present before any call of the function
Calling a function for which the compiler has not yet seen a declaration or definition is an error
return-type function-name ( parameters );
double average(double, double);
double average(double a, double b);
double average( );
double average(double a, double b);
Typically for defensive purposes
• If a macro happens to have the same name as
a parameter, the parameter name will be
replaced during preprocessing
• Damage the prototype in which it appears
19. Function Prototypes
• Put all of the function definitions before main don’t cover all problems
Some of the functions may call each other
Two functions can call each other
It won’t be feasible to put all the functions in one file
That’s why the function prototype is needed
• This declaration informs the compiler average returns a double value but
provides no information about the number and types of its parameters
• This form of function declaration is the only one allowed in K&R C
• The function prototype which parameter information is included was introduce
in C89
double average();
20. Function Declarations in the Body
• Nested Function Definitions
Some programming languages allow procedures and functions to be nested
within each other
C does not permit the definition of one function to appear in the body of another
• Function Declarations in the body of other functions
Is legal
The declaration is only valid in the body of this function
It is clearer to the reader which functions call which other functions
It can be a nuisance if several functions need to call in the same function
Maintenance may be hard
int main(void)
{
double average(double a, double a);
…
}
21. Combining Function Declarations
• If several functions have the same return type, we can do the combined
functions declarations
• Isn’t a good idea since it can easily cause confusion
void print_pun(void);
void print_count(int n);
void print_pun(void), print_count(int n);
22. 9.3 Arguments (1/2)
• Parameters appear in function definitions
Dummy names that represent values to be supplied when the function is called
• Arguments are expressions appear in function calls
• Pass by value
When a function is called, each argument is evaluated and its value assigned to
the corresponding parameter
Any changes made to the parameter during the execution of the function don’t
affect the argument
23. 9.3 Arguments (2/2)
• Pass by value
1. Advantage
We can use parameters as variables within the function
Reducing the number of genuine variable needed
2. Disadvantage
Since a function can’t return two numbers, we might try passing a pair of variables to the
function and having it modify them: Pass by address Pass by address
Ch11.4
24. 9.3 Arguments: Argument Conversions
• C allows function calls in which the types of the arguments don’t
match the type of the parameters
• Rules for argument conversion
1. The compiler has encountered a prototype prior to the call
The value of each argument is implicitly converted to the type of the corresponding
parameter as if by assignment
2. The compiler has not encountered a prototype prior to the call
The compiler performs the default argument promotions
a. float argument are converted to double
b. The integral promotions are performed
− char and short arguments to be converted to int
Rely on the default argument promotions is dangerous
int main(void)
{
double x = 3.0;
printf(“Square: %dn”, square(x) );
return 0;
}
int square(int n )
{
return n * n;
}
• The compiler expects an argument of type int for square
• But has given a double value instead: Calling square is undefined
1. Casting
2. Provide a prototype
before calling functions
25. 9.3 Arguments: Array Arguments (1/2)
1. When a function parameter is a one-dimensional array
• The length of the array can be left unspecified
• The length of an array needed to pass as an additional argument
• A function has no way to check the correctness of array length
• We can also omit the parameter names for the prototype
• Don’t put brackets after an array name when passing it to a function
• A function is allowed to change the elements of an array parameter
The change is reflected in the corresponding argument
int f (int a[ ])
/* no length specified */
{
…
}
sizeof cannot give the correct answer for an array parameter
int sum_array(int a[], int n);
int sum_array(int [], int );
total = sum_array(b[], LEN); /* WRONG */
Be careful not to tell a function that an array argument is larger than it really is
Will cause undefined behavior
Ch12.3
26. Specify a Length for A One-Dimensional Array
Parameter
• The compiler will ignore it
• Will misleading in that it suggests that inner_product can only be passed
arrays of length 3, when in fact we can pass arrays of arbitrary length
• Only be helpful for documentation
double inner_product (double v[3], double w[3]);
27. 9.3 Arguments: Array Arguments (2/2)
2. When a function parameter is a multidimensional array
• Only the length of the first dimension may be omitted when the parameter is
declared
Can work around this difficulty by using arrays of pointers
C99’s VLA parameters provide an even better solution to the problem
Arrays of pointers
Ch13.7
Why only the first dimension in an array parameter can be left unspecified?
• When a array is passed to a function, the function is given a pointer to the first element in the array
• Compiler needs the length of other dimensions to compute the address
28. 9.3 Arguments: Variable-Length Array
Parameters
• There is no direct link between n and the length of the array a
• Using a variable-length array parameter, we can explicitly state that VLA b’s
length is n
The value of the first parameter (n) specifies the length of the second parameter (b)
Order is important
There are several ways to write the prototype for this function
The length of VLA can be any expression
There is no additional error-checking
Also can be used in multidimensional VLA
C99
int sum_array (int a[ ], int n) {
…
}
int sum_array (int n, int b[n]) {
…
}
int sum_array (int n, int c[n]); /* version 1 */
int sum_array (int n, int d[*]); /* version 2a */
int sum_array (int n, int [*]); /* version 2b */
int sum_array (int n, int e[ ]); /* version 3a */
int sum_array (int , int [ ]); /* version 3b */
int concatenate (int m, int n, int a[m], int b[n], int c[m+n])
29. 9.3 Arguments: Using static in Array Parameter
Declarations
• C99 allows to use the keyword static in the declaration of array
parameters
Allow a C compiler to generate faster instructions for accessing the array
− It can arrange to “prefetch” these elements from memory when the function is called,
before the elements are actually needed by statements within the function
static can be used only in the first dimension
C99
int sum_array (int a[static 3], int n)
/* The length of array a is guaranteed to be at least 3 */
{
…
}
30. 9.3 Arguments: Compound Literals
• In C99, we can using compound literal
Unnamed array that is created “on the fly” by simply specifying which elements it
contains
Can determine the length of array or not which will be determined by the number of
elements in the literal
Consist of a type name within parentheses, followed by a set of values enclosed by braces
Compound literals and initializers obey the same rules
− A compound literal may contain designators
− Any uninitialized elements default to zero
Created inside a function may contain arbitrary expressions, not just constants
Is an lvalue
− The values of its elements can be changed
Can be made “read-only” by adding the word const to its type
C99
int b[ ] = {3, 0, 3, 4, 1};
total = sum_array(b, 5);
total = sum_array((int []){3, 0, 3, 4, 1}, 5);
(int[10]) {8, 6}
designated
initializers
Ch8.1
lvalues
Ch4.2
31. 9.4 The return Statements
• A non-void function must use the return statement to specify what value it will return
• The expression is often just a constant or variable
• More complex expressions are possible
• If the type of the expression in a return statement doesn’t match the function’s return
type
Implicitly conversion
• return statements may appear in functions whose return type is void, provide that no
expression is given
• If a non-void function reaches the end of its body and fails to execute a return statement
The behavior of the program is undefined if it attempts to use the value returned by the function
“control reached end of non-void function” in C89
return expression;
Why happens if a non-void functions attempts to execute a return
statement that has no expression?
• In C89, causes undefined behavior
• In C99, is illegal and should be detect as an error by the compiler
The compiler notice that function
doesn’t have a return statement
32. return Statements with Parentheses
• Why do some programmers put parentheses around the expression in a
return statement?
The examples of the first edition of K&R always have parentheses in return
statements
33. 9.5 Program Termination
• main function must have a return type
The return type of main is normally int
Older C programs often omit main’s return type
− Default return type is int
Can’t omit main’s return type in C99
− Omit the word void in main’s parameter list remains legal
− There are sometimes two main’s parameters: argc and argv
•The value returned by main is a status code
Can be tested when the program terminates in some operating systems
1. Return 0 if the program terminates normally
2. Return a value other than 0 to indicate abnormal termination
argc and argv
Ch13.7
34. 9.5 Program Termination: The exit Function
• Another way to terminate the program is to call the exit function
Belongs to <stdlib.h>
The argument passed to exit has the same meaning as main’s return value
− EXIT_SUCCESS and EXIT_FAILURE are macros defined in <stdlib.h>
− EXIT_SUCCESS is 0 while EXIT_FAILURE is 1
• The difference between return and exit is that
exit causes program termination regardless of which function calls it
The return statement causes program termination only when it appears in the
main function
<stdlib.h>
header
Ch26.2exit (0); /* normal termination */
exit (EXIT_SUCCESS); /* normal termination */
exit (EXIT_FAILURE); /* abnormal termination */
35. 9.6 Recursion
• A function is recursive if it calls itself
• Some programming languages rely heavily on recursion, while others
don’t even allow it
C falls somewhere in the middle
• All recursive functions need some kind of termination condition in
order to prevent infinite recursion
int fact (int n)
{
if ( n <= 1 )
return 1;
else
return n * fact (n – 1);
}
36. 9.6 Recursion: The Quicksort Algorithm
• Recursion is more helpful for sophisticated algorithms that require a
function to call itself two or more times
Often arise as a result of an algorithm design technique know as decide-and-
conquer
− A large problem is divided into smaller pieces that are then tackled by the same
algorithm
• Quicksort
Randomly choose one number called as pivot
(Partition) Rearrange sequence to let:
− All of the numbers in the pivot’s left are smaller than pivot
− All of the numbers in the pivot’s right are larger than pivot
Sort every sub-sequence recursively until there is no sub-sequence
The original partition scheme described by C.A.R. Hoare uses two indices that start at the ends of the array being partitioned, then move toward each other, until they detect an inversion.
Hoare’s scheme is more efficient than Lomuto’s partition scheme because it does three times fewer swaps on average, and it creates efficient partitions even when all values are equal.
hoare的partition实现方式
i从前往后找到大于pivot的元素,j从后往前找到小于pivot的元素,然后两者swap.
Lomuto partition
This scheme is attributed to Nico Lomuto and popularized by Bentley in his book
lomuto的partition实现方式
i指示最前面的大于pivot的元素位置,j从前往后滑动来调整元素位置。每次j碰到小于pivot的元素,则swap i位置的元素和j位置的元素,再i指向下一个大于pivot的元素。最后,记得swap i位置的元素和最末尾的元素。