Part 4. Functions

You'll use functions everywhere in C. They are a handy way to make your code simpler and easier to read. Functions also make difficult problems easier to tackle by breaking the big problem into smaller chunks.

Throughout this video series, we've seen main as a function, but we haven't yet learned how to define your own functions. But now we're ready!

You define a function the same way you define the main function: indicate its type, then name the function followed by parentheses. And you put your function statements inside curly braces. End your function with a return to pass a final value back to the calling program. For example, a trivial "Hello world" function would look like this:

int
hello()
{
   puts("Hello world");
   return 0;
}

Having written that function, you can call it in your program the same way you would call the other standard C functions we've used: just list its name.

#include <stdio.h>

int
hello()
{
   puts("Hello world");
   return 0;
}

int
main()
{
   hello();
   return 0;
}

You can write the function the same way you would write the main function. Define any variables you need at the start of your function. A neat feature with functions is that any variable name you use in a function is separate from variables you use in other functions, or even the main function. Variables are local in scope, which means that if you define a variable as part of a function, it's only part of that function. You can even give a variable in a function the same name that you used in the main function.

Here's a simple example, where we call a function named loophello from a for loop. The iteration variable iter is used in both main and the loophello function:

#include <stdio.h>

int
loophello()
{
   int iter;

   /* print Hello three times */

   for (iter = 1; iter <= 3; iter = iter + 1) {
      printf("%d - Hello!\n", iter);
   }

   return 0;
}

int
main()
{
   int iter;

   /* loop three times */

   for (iter = 1; iter <= 3; iter = iter + 1) {
      printf("Loop %d\n", iter);
      loophello();
   }

   return 0;
}

Sample output from this program:

Loop 1
1 - Hello!
2 - Hello!
3 - Hello!
Loop 2
1 - Hello!
2 - Hello!
3 - Hello!
Loop 3
1 - Hello!
2 - Hello!
3 - Hello!

Pass values to a function using arguments. You define them in the parentheses of your function definition. For example, this sample function will add 1 to the integer that's passed to it, and print that number to the screen before returning.

int
addone(int i)
{
   int n;

   /* add one to the number and print it */
   n = i + 1;
   printf("n is %d\n", n);

   /* return the value of i+1 */
   return n;
}

Variables defined this way also have another feature: they don't keep their values between calls to the function. If you call the addone function multiple times with the same argument, addone will always print the same value.

#include <stdio.h>

int
addone(int i)
{
   int n;

   /* add one to the number and print it */
   n = i + 1;
   printf("n is %d\n", n);

   /* return the value of i+1 */
   return n;
}

int
main()
{
   addone(1);
   addone(1);
   addone(1);

   return 0;
}

If you compile this program and run it, you'll see that it always prints the same value of n. That's because n is always one more than the value of the argument that was passed to it, and we always called addone with 1.

n is 2
n is 2
n is 2

If you need to pass additional values to your function, the easiest way is to add more arguments to your function. Separate each variable definition in your function declaration with a comma. For example, here is a simple function that prints a sequence of numbers between two start and stop numbers:

#include <stdio.h>

int
sequence(int start, int stop)
{
   int iter;

   /* print a list of numbers from 'start' to 'stop' */

   for (iter = start; iter <= stop; iter = iter + 1) {
      printf("%d ", iter);
   }

   printf("\n");

   return 0;
}

int
main()
{
   int i;

   /* print five sequences of ten numbers, starting at 1, 2, 3, 4, 5 */

   for (i = 1; i <= 5; i = i + 1) {
      sequence(i, i + 10);
   }

   return 0;
}

If you compile and run this sample program, you will see this list of numbers:

1 2 3 4 5 6 7 8 9 10 11 
2 3 4 5 6 7 8 9 10 11 12 
3 4 5 6 7 8 9 10 11 12 13 
4 5 6 7 8 9 10 11 12 13 14 
5 6 7 8 9 10 11 12 13 14 15 

In the sample programs I've shown so far, I've been careful to write the functions before the first time they are called in the main function. Doing so means the compiler "knows" about the function and how you should call it by the time it is actually used in the program.

But this is not always possible. And sometimes, you may find it more convenient to write your main function first in the file, then write the functions that main needs to call. When you do this, you need to tell the compiler how each function is called. To do this, you need to write a function declaration, which is just a one-line definition of your function and the variables it uses as parameters. Here's the same program from above, written with the main function first, then the sequence function. When you write your programs this way, put the function declarations after the #include statements and before the main function:

#include <stdio.h>

int sequence(int start, int stop);

int
main()
{
   int i;

   /* print five sequences of ten numbers, starting at 1, 2, 3, 4, 5 */

   for (i = 1; i <= 5; i = i + 1) {
      sequence(i, i + 10);
   }

   return 0;
}

int
sequence(int start, int stop)
{
   int iter;

   /* print a list of numbers from 'start' to 'stop' */

   for (iter = start; iter <= stop; iter = iter + 1) {
      printf("%d ", iter);
   }

   printf("\n");

   return 0;
}

Technically, the function declaration does not need to use the same variable names as the function itself. We could just as easily written int sequence(int a, int b); for the declaration and that would have worked fine. However, it's a good idea to use the same variable names, so you (the programmer) don't get confused when you look at the code again later.

Functions help tackle large problems

Functions allow you to tackle a large problem and make it more manageable by breaking it up into chunks. Each "chunk" is a function that has specific functionality.

Functions also help make your code easier to read. For example, do you remember the "even" program that asked the user for a value, then printed whether or not that number was even or odd? The if test used a modulo (%) operator to test if the number was even. We can improve on the "is it even?" program by using a function to test if a number is even.

The is_even function does the same modulo calculation, and returns the value of the test. The if statement in the main function uses this value to test if the number is even:

#include <stdio.h>

int
is_even(int i)
{
   /* print a message so we know we made it into the function */
   puts("[is_even]");                  /* debugging */

   /* test if the number i is even; return True if it is */
   return (i % 2 == 0);
}

int
main()
{
   int num;

   do {
      puts("Enter a number: (0 to quit)");
      scanf("%d", &num);

      if (is_even(num)) {
         printf("%d is even\n", num);
      }
      else {
         printf("%d is odd\n", num);
      }
   } while (num != 0);

   return 0;
}

You can call a function anywhere in your program—even inside other functions. But be careful about a function calling itself. This is called recursion and is allowed in C. When using a recursive function, it's important that the function break down the problem by one "step," and that the function should have a defined "stopping point."

Knowing when to stop is critical. Otherwise, your program will loop forever.

Here's an example of a recursive function. The Fibonocci Sequence is defined in mathematics as an iteration, where each iteration is the sum of the two iterations before it. And the "Fibonacci" of 0 and 1 are defined as and 1, respectively. (You cannot have negative values in the Fibonacci sequence.)

#include <stdio.h>

int
fib(int i)
{
/*
Fibonacci sequence

fib(0) = 0;
fib(1) = 1;

fib(n) = fib(n-1) + fib(n-2);

for any n greater than or equal to zero
... but really for any n >= 2
*/

   switch (i) {
   case 0:
      return 0;
      break;

   case 1:
      return 1;
      break;

   default:
      return (fib(i - 1) + fib(i - 2));
   }
}

int
main()
{
   int n;
   int result;

   puts("Enter number to start Fibonacci sequence:");
   scanf("%d", &n);

   if (fib >= 0) {
      result = fib(n);
      printf("fib(%d) = %d\n", n, result);
   }
   else {
      puts("error! Number must be 0 or greater");
   }

   return 0;
}

Standard C library of functions

Now that we've learned about functions and how to write your own functions, let's go back to examine the functions that we've been using from the standard C libray. These are all defined in that stdio.h that we keep using at the start of our programs:

puts("string")
Prints a string to the user
printf("%d, %d, %d, …", a, b, c, …)
Prints a formatted string, using a format string with % placeholders to define values
scanf("%d …", &var …)
Scans (reads) a value from the user, using a format to define what kind of value to read

The stdio.h is actually called a header file or include file and contains a bunch of definitions for functions that control input and output. The "io" in the name stands for "input" and "output." The "std" in the name is an abbreviation for "standard" because these are part of the standard C library of functions.

There are lots of other useful functions in the standard C library. A few others that you will find immediately useful are:

getchar()
Get a single character from the user. This is a better way to read a character than scanf. This actually returns an int value, but you can treat it like a char character value.
putchar(int ch)
Put a single character back to the user, usually to the screen unless the user has redirected the output of the program to a file (at the command line, when they run the program). The character is stored in an int value but you can think of it as just a single character.

And a few other standard C library functions from the stdlib.h header file, which you might find useful now:

rand()
Returns a random int somewhere between 0 and a very large number, defined by RAND_MAX (which is #define'd elsewhere in the stdlib.h file).
srand(int seed)
Sets the "seed" value of the random number generator. Use this before calling the random() function.
exit(int status)
Exits the program with a status code. This is similar to calling return status; in your main function, but you can call exit anywhere in your program—even in other functions. If things go really wrong in a function, you can end the program with exit. But try to write your programs so only main is the function that exits the program, using return.
abs(int num)
Returns the absolute value of a number. If you give abs a negative number, the function will return the positive value of it.

If your program relies on mathematical functions, especially trigonometry, you might be interested in these useful standard C library functions that are defined in the math.h header file. These all return double values, which we learned in Part 2. Introduction to C programming are double-precision floating point values:

sin(double x)
sinh(double x)
asin(double x)
The sine and hyperbolic sine of a radian angle x. Also the arc sine of x, in radians.
cos(double x)
cosh(double x)
acos(double x)
The cosine and hyperbolic cosine of a radian angle x. Also the arc cosine of x, in radians.
tan(double x)
tanh(double x)
atan(double x)
The tangent and hyperbolic tangent of a radian angle x. Also the arc tangent of x, in radians.
atan2(double y, double x)
Another way to calculate the arc tangent. Returns the arc tangent in radians of y/x and places the result in the correct quadrant.
pow(double x, double y)
The value of x raised to the power of y. For example, pow(2.0, 3.0) is the same as 2³ ("two cubed") and pow(4.0, -1.0) is the same as ¼ ("one-quarter").
sqrt(double x)
The square root of x. This is basically the same as calling pow(x, 0.5) because any value raised to ½ is the square root. Be careful about square roots of negative numbers.
exp(double x)
The value of ℯ raised to the power of x, where ℯ is the mathematical constant 2.718281… This is basically the same as calling pow(2.718281, x)
log(double x)
The natural logarithm function, using ℯ. For example, x = exp(3.0) followed by log(x) will return 3.0.
log10(double x)
The base-10 logarithm function.

If you are using math.h functions with the GCC compiler, you will need to compile with -lm at the end of your command line. This will link (-l) the math library, called libm somewhere on your system. For example, using IA-16 GCC on DOS:

I16GCC -Wall -o MYMATH.EXE MYMATH.C -lm

..and GCC on Linux:

gcc -Wall -o mymath mymath.c -lm

Other C compilers may not require this extra step, and may recognize that it needs to link the math library automatically. I mention it because I'm using the GCC compiler in my videos. If in doubt, check the manual for your compiler.

PRACTICE

Now that you've learned about the basics of writing C functions, let's practice that with a few exercises:

Practice program 1.

Write a program that calculates the factorial of a number, using a function int fact(int n). The factorial (𝓃!) of a non-negative integer 𝓃 is defined as the product (multiplication) of that number times all the numbers down to 1. The factorial of zero (0!) is 1, and the factorial of one (1!) is also 1. For example, the factorial of five (5!) is 5×4×3×2×1.

Practice program 2.

Write a program that calculates the factorial of a number, using a recursive function int fact(int n). This recursive function should calculate fact(n) for any non-negative n as n * fact(n-1), where fact(0) is 1.

For each practice program 1 and 2, what should you return for the factorial of a negative number?

Practice program 3.

In Part 3. Flow control, we wrote a simple version of the FreeDOS PAUSE command, using scanf to read input from the user, one character at a time. Re-write the FreeDOS PAUSE command without using scanf. Display a prompt "Press Enter to continue…" and call the getchar function until you find a newline (\n) character.

Need help? Check out the sample solutions.