Introduction to C Programming - Part 4

From EDM2
Revision as of 02:50, 6 June 2017 by Ak120 (Talk | contribs)

Jump to: navigation, search
Introduction to C Programming
Part: 1 2 3 4 5 6 7 8 9 10

by Björn Fahller

So, I've volunteered to continue this series, now when Carsten no longer has the time needed for it. The topic for this month is functions and separate compilation.

Definition

What exactly is a function? If you've studied mathematics, you probably have a clear idea of what a function is. It is something which you put a value into, and get a value (most probably a different value) from. In C, and most other programming languages, a function is that, and then some. All (useful) functions have at least one of the following three characteristics:

  • They use input, often called arguments or parameters.
  • They produce an output, often referred to as the return value.
  • They have side effects.

A side effect can for example be printing on the screen, modifying a global variable or reading a file. A mathematical function never has side effects. Another way of seeing a function, is to say that it's a small program within the program.

In the examples from previous issues, the function "printf" has already been used extensively. In a mathematical sense, "printf" is not a function, since its return value is not useful, other than as an error check. What's interesting about "printf" is its side effect; the writing on the screen. "printf" is a predefined standard function.

While predefined functions are useful and interesting in their own right, the C language would never have been successful if you couldn't write your own functions, and that is exactly what this month's lesson is about.

Reason

Assume that you need to calculate the sum of all integers between two numbers inclusively. From the earlier lessons, you know that you can do this as follows:

#include <stdio.h>

int main(void)
{
  int first = 10; /* for example */
  int last = 20;
  int loopvar = 0;
  int answer = 0;
  for (loopvar = first; loopvar <= last; loopvar++)
  {
    answer = answer+loopvar;
  }
  printf("The sum of all integers between %d and %d is %d\n",
         first, last, answer);
  return 0;
}

If you need the sum in more than one place, it's time to create a function, so let's do this.

Prototype

The first thing to do is to think of the three common things with functions. Which parts do we want, and how? Do we want input? Yes, we do. What input? I think a good choice is the lower and upper limit. Do we want output? Yes we do. What output? The sum. Do we want side effects? Maybe, but in this case I don't think so. From this we can decide something about the function. It must accept two numbers, the lower and upper limit, and it must return one value, the sum. The previous sentence is pretty much the English language version, of what in C is called a function prototype. The C prototype for the function looks like this:

int sumOfIntegersBetween(int lower, int upper);

A re-translation from C to English reads: "sumOfIntegersBetween, is a function that takes two arguments, an integer called lower, and an integer called upper, and the function returns an integer." Note that it doesn't say anything about what the function actually does, or if it has side effects or not. This is a matter of choosing a good descriptive name. It's only in the name and in documentation, you can tell about side effects. Had the function printed the sum on the screen instead of returning it, a better name would have been "printSumOfIntegersBetween."

A Function

The function itself looks very much like the small program example above:

int sumOfIntegersBetween(int lower, int upper)
{
  int answer = 0;
  int loopvar = 0;
  for (loopvar = lower; loopvar <= upper; loopvar++)
  {
    answer = answer + loopvar;
  }
  return answer;
}

"lower" and "upper" are the parameters of the function. They get the values the function is called with (the arguments). With the help of this, the example program can be written as:

int main(void)
{
  int first = 10;
  int last = 20;
  int sum = sumOfIntegersBetween(first, last);
  printf("The sum of all integers between %d and %d is %d\n",
         first, last, sum);
  return 0;
}

Here, when "sumOfIntegersBetween" is called, the "lower" variable gets the value 10, and the "upper" variable gets the value 20. The reason it is "lower" that gets the value 10 is that "lower" is first in the list, and "upper" becomes 20 because it's second in the list.

So, what is the gain in this? Well, maybe a bit of clarity, but that's it. However, if we expand the program a bit, to make use of several sums, a lot is gained. Try, as an exercise in loops, to write this without functions:

int main(void)
{
  int first = 10;
  int second = 20;
  int third = 30;
  int fourth = 40;
  int sumFirstSecond = sumOfIntegersBetween(first, second);
  int sumThirdFourth = sumOfIntegersBetween(third, fourth);
  printf("The sum of integers between %d and %d is %d\n",
         sumFirstSecond, sumThirdFourth,
         sumOfIntegersBetween(sumFirstSecond, sumThirdFourth));
  return 0;
}

This program first calculates the sum of all integers between 10 and 20 (which is 165) and calls it sumFirstSecond, and then the sum of all integers between 30 and 40 (which is 385) and calls it sumThirdFourth. Then, the sum of all integers between sumFirstSecond (165) and sumThirdFourth (385) is calculated.

Note how the last call to "printf" is a call to our function "sumOfIntegersBetween". At first this might seem strange, but what it means is that the last argument to printf is really the value returned by the call to sumOfIntegersBetween.

Separation

Now to the next stage, which will be the last part for this month. I mentioned prototypes very briefly, and maybe you wondered if they have any purpose, and if so, what? Well, they do indeed have a purpose, but with the small programs written so far, it hasn't surfaced. The good thing about prototypes comes when you separate your program into several files. If your program is large, you want to separate it into several files, because going back and forth in one enormous file is not fun, and recompiling it all, just because you missed a detail when altering one function takes unnecessary time. It also comes in handy if you want to use the same function in another program.

So, enough talk. Let's create 3 files.

First the file "integers.h" with the following contents:

int sumOfIntegersBetween(int lower, int upper);
/* calculates the sum of all integers between lower
 * and upper inclusively. The function does not have
 * any side effects.
 */

Then the file "integers.c" which contains:

#include "integers.h" /* our sumOfIntegersBetween prototype */

int sumOfIntegersBetween(int lower, int upper)
{
  int answer = 0;
  int loopvar = 0;
  for (loopvar = lower; loopvar <= upper; loopvar++)
  {
    answer = answer + loopvar;
  }
  return answer;
}

And finally file "lesson4.c":

#include <stdio.h>
#include "integers.h" /* our sumOfIntegersBetween prototype */

int main(void)
{
  int first = 10;
  int second = 20;
  int third = 30;
  int fourth = 40;
  int sumFirstSecond = sumOfIntegersBetween(first, second);
  int sumThirdFourth = sumOfIntegersBetween(third, fourth);
  printf("The sum of integers between %d and %d is %d\n",
         sumFirstSecond, sumThirdFourth,
         sumOfIntegersBetween(sumFirstSecond, sumThirdFourth));
  return 0;
}

If you're using VisualAge C++, compile as follows:

icc lesson4.c integers.c

A test-run of the program shows:

[d:\]lesson4.exe
The sum of integers between 165 and 385 is 60775

If you want to use the sumOfIntegersBetween function in any other program, include the header "integers.h" in it, which tells the program how to handle the call to sumOfIntegersBetween. Then compile with "integers.c" (the inclusion of the header just tells the compiler how to handle the call itself, not where to find the function.) In "integers.c" and "lesson4.c" the purpose of including the header file "integers.h" is different. For "lesson4.c" the inclusion of the prototype allows the compiler to check if you call the function "sumOfIntegersBetween" with the right number of arguments of the right type. For "integers.c" the prototype is used by the compiler for comparison. If the prototype differs from the function, the compiler will report an error.

Something called "separate compilation" will improve our example a bit. What this means is that we compile the parts separately, and then link them together. With VisualAge C++ the compilation to a .OBJ file, which can be linked to a .EXE file later, is accomplished with the "/c" flag:

[d:\]icc /c lesson4.c
[d:\]icc /c integers.c
[d:\]ilink lesson4.obj integers.obj

This again results in "lesson4.exe" which can be run as above. This may seem like an unnecessarily complicated method of doing the same thing as before, but there is an advantage to it. If you make a change to "lesson4.c" you only need to compile it again, and then link with the old copy of "integers.obj." If you need the function "sumOfIntegersBetween" in any other program, all you need to do is to include "integers.h" in the beginning of it, and link with the "integers.obj" that you've already created. With this small program, little is gained, but when programs grow large, the compile time due to a small change is drastically reduced.

Recap

A function is a small program within a program. Characteristic for all useful functions is at least one of the following three:

  • They accept input, called arguments or parameters.
  • They produce output, called the return value.
  • They have side effects.

A function is known through its prototype, which tells the name of the function, what it returns and what parameters it accepts. The prototype does not say what the function does, or if it has side effects. That is a matter of good naming and documentation.

A function can be put in a separate file, to make it possible to use together with any other program. When doing so, the function prototype should be defined in a header file, which the program that uses the function includes. The prototype allows the compiler to check if you pass the right number and types of arguments to the function.

Next Month

I guess next month it's time to take a look at the different types of variables and parameters you can have, and how to declare our own (yes, we can declare our own types.) However, feel free to e-mail me suggestions if you have other ideas.