C development on Linux – Functions – V.

Introduction

The C standard library offers a plethora of functions for many usual tasks. Also there are lots of libraries for extra functionality, like GUI design (GTK+) or database interfacing (libpq). However, as you advance in the C programming world, you will soon find yourself repeating the same instructions in the same order over and over again and that will become time-consuming and inefficient. So you can just wrap all those instructions in a function and just call said function when you need it. Here’s what you’re gonna learn by reading this article, plus some useful tips that will make your life easier.

Creating your functions

For a simple start, let’s say you wanna write a calculator. We won’t focus on the interface (GUI vs curses vs slang vs CLI) as we’re interested on the internals. It would be clunky to not create a function for every operation you decide to support, unless there is one already, like pow() , defined in math.h, which returns the result of a base raised to a power. So, for example, for addition you will have a function named add() that takes two arguments, at least for now, and returns the result. So when the user chooses to add the number (s)he introduced, you just call the function with the numbers the user entered and you needn’t worry about anything else. These three terms that I wrote in italics are essential in understanding functions. A function usually (but not always) takes something, does a number of operations on that something and spits out the result. “Not always” because main(), as you could see before, can be called with no arguments, and there are other examples as well. But for now, let’s focus on our examples. The numbers that need to be added together are the arguments, that “something” you give the function for processing. The processing part is in the body of the function, when you tell it to add the numbers together. After that, the “spitting out” part is called returning a value, which is, in our case, the result of the addition.

Let’s see what we talked about in a practical example:

#include <stdio.h>
/* this contains the definition of printf()*/
double add(double x, double y);

int main()
{
  float first, second;
  printf("Please enter the first number.\n");
  scanf("%F",&first);
  printf("Please enter the second number.\n");
  scanf("%F",&second);
  
  double add(double a, double b)
  {
    return a + b;
  }
  
  printf("The result of the addition is %F\n", add(first, second));
  
  return 0;
}

The above code, although simplistic at best, helps us point out precisely what we talked about before. First we declare the function, before main(), and the purpose is to know the name, the type of the arguments and the type the function returns. This line is also called defining the function prototype. As you can see, the arguments’ names from the declaration need not be the same as the ones used in the definition, but if that troubles you, use a constant naming scheme, it’s alright. Before we use the function we must define it, as in tell the world what is it exactly that it does. Even if the function’s body is single-line, just as it is in our example, it is best you use braces for readability and for good habit. Here, everything the function does is return the result of the addition between two numbers.

C function prototype, definition,call

We suggest you use names for functions, arguments and ordinary variables or constants that reflect what they do, again for good habit and for sparing the programmers reading your code the attempts to guess what variable “xyzgth” does or is used for. Also, use comments. Even if in the above code comments might seem overkill, they’re not. When you look at the code two months later, you won’t have any idea what was in your mind when you wrote the code. So use and abuse comments, they’ll save you, trust me.

Exercise

There are functions that can accept a variable number of arguments, like printf() for example. You are allowed to use Google to see what they do and try to rewrite the add() function to accept more than two arguments, or create another function. You can also use “man 3 printf”.


We told you before that main() can be called with no arguments. Of course, that means it can be called with arguments as well. When is this useful? In programs simple such as ours, since we call them with no arguments, the parentheses of main() are empty. But when your programs will grow in complexity, especially if they will be command-line oriented you will need to add the functionality of arguments, like gcc’s -v flag that prints the version. When such functionality is desired, main() must have arguments, two to be precise. The main function becomes

int main(int argc, char **argv)
{
  ....
}

Before you freak out over the cryptic names and the double asterisks, wait till you get the explanation, which is actually simple. The first argument is an integer named argc, and the name comes from “ARGument Count”. A little better, right? About the second argument…well, the name stands for “ARGument Vector” officially and it’s a pointer to a pointer to a char. Now, in English, while argc stores the number of arguments, argv stores the arguments as a series of strings. The “pointer to…” part will be explained in the next part of the article, for now all you need to know is that if, for example, the user will type three arguments to the program, index zero of argv will be the name of the program itself, index one will store the first argument to the program and so on. This is how you can use a switch/case to check for the arguments passed to your programs. Before we give you a short example, we feel compelled to tell you that main has two arguments as defined by the standard, and this is how it’s used on most Linux and Unix systems. However, if you (will) work on Windows or Darwin, main() will have one or two more arguments, but those are system-dependent and so are not defined or required by the standard. Also, “char **argv” might also be written as “char *argv[]”. You will see both, depending on the developer’s preference.

You might remember we told you in the first part of our series how we’re gonna use Kimball Hawkins’ yest program for examples. It’s time we start, so here’s how yest deals with a part of the possible user input:

if ( strncmp( argv[i], "--help", 6 ) == 0 ||
	     strncmp( argv[i], "-?",     2 ) == 0 ||
	     strncmp( argv[i], "?",      1 ) == 0 ||
	     strncmp( argv[i], "help",   4 ) == 0 )
	    yest_help();	/* help requested, display it */

if ( strncmp( argv[i], "--version", 9 ) == 0 ||
	     strncmp( argv[i], "--license", 9 ) == 0 )
	    yest_version();	/* version/license information requested */

You may see in this code how Kimball comments his code, although the name of the functions he calls – yest_help() and yest_version() – are pretty self-explanatory. The standard strncmp() function, to be found in string.h, compares two strings, in our case argv[i] and “help”, for example, but only the first x characters (4 in the “help” line) and returns zero if the first string matches the second.

main arguments

Exercise

How would you use switch/case to check if the first argument is “–help” and the second is “–version”? Can these options be used together? How would the code differ?

Things to be aware of

C does not allow you to define a function inside another, with the exception main(), which is, as we can see, special. Be also aware that what you define inside a function “lives” inside a function only. So you can have a variable named “a” defined inside three different functions with no problems at all, but that may lead to issues in larger programs, so we don’t recommend it.

Custom header files

As your programs will grow bigger and bigger, you’ll find the need to split them. You can have more than one source files, but you can also write your own headers. So going back to our addition program, you can create a header named operations.h which will have the line “double add(double x, double y);”, so your program will deal only with the definition, the part where you say that add() will return a + b. Including your custom header is done just as you include system-installed ones with one important exception: remember to use double quotes instead of angle brackets, like this: “#include “operations.h””. This header can be placed in the directory where the other source files are stored or in another path, specified as an argument to gcc so it would know where to look. Header files can contain also constants definitions (with #define) or other declarations, as long as you know that they will be used in every source file of the program. It’s not mandatory, it’s just good practice. So, how would you write a calculator that deals only with the basic arithmetic operations and uses headers?

Recursive functions

As we expect you to have some programming background, we’re certain that you know what recursive functions are and how/when to use them. This is why this subchapter will be shorter than it would normally be. In short, one says about a function to be recursive when it calls itself. Although the concept might be daunting for new programmers, one simpler, real-life way recursion can be explained is this: try sitting between two mirrors facing each other. The effect you see is a visual representation of recursion. But we will give you a short example, so that you understand better when and how to use it. You probably remember from school when you were taught about factorials. A factorial is the product of a all integers smaller than it or equal, as long as they’re greater than zero. The notation for this is an exclamation sign, so 6! = 6*5*4*3*2*1=720. How can we do this in C in the most efficient manner? Of course, using recursion.

int factorial(int number)
{
  if(number <= 1)
    return 1;
  else
    return number * factorial(number-1)
}

Conclusion

We recommend you use functions as often as possible, and put their prototypes in header files as often, because your code will be more organized and your work will become easier. Speaking of headers, we leave it as a final exercise for you to start reading the header file defining mathematical operations (math.h) to get an idea how it looks like and what it contains. Then use it in order to improve the calculator with some improved functionality beyond basics.

Here is what you can expect next:



Comments and Discussions
Linux Forum