[Courses] C Programming For Absolute Beginners, Lesson 5A: All About Functions, Part 2

Jacinta Richardson jarich at perltraining.com.au
Tue Apr 3 13:18:47 UTC 2012


Adding to the pile.

On 02/04/12 15:06, Carla Schroder wrote:
> C Programming For Absolute Beginners, Lesson 5A: All About Functions, Part 2

> Functions are called from other functions, and perform specific tasks. If you
> find that a function you are writing is getting fat and unwieldy, it might be a
> sign to re-think what you're trying to make it do. Functions make it possible
> to write nice modular programs that are easier to understand and modify.

The example I usually share with my students is this.  Think about what steps 
are required to make a cup of instant coffee.  I then ask the students to help 
me brain storm the steps, it usually ends up with something like this:

     1. Assemble everything (cup, spoon, coffee, kettle, milk, water etc)
     2. Put water in the kettle and boil [*]
     3. Put coffee in cup
     4. Pour hot water into cup [*]
     5. Add milk (if desired) [*]
     6. Stir [*]

Here we've broken down the task into 6 steps (we could have a few more or a few 
less).  What is more, the ones which have asterisks all apply to making tea, or 
hot chocolate, too!  So we could write (using pseudo-code):

    // I know that this is not an appropriate argument for main() it's
    pseudocode, I'm cheating.
    yummybeverage main(sometype beverage_selection) {

         // gather all the things together:
         sometype kettle;
         sometype water;
         sometype boiling_water;
         sometype cup;
         sometype milk;
         sometype spoon;

         // boil the kettle
         kettle = add_to(water, kettle);
         boiling_water = boil(kettle);          // in Australia we have electric
    kettles that sit on your counter top

         // add stuff to the cup
         cup = add_to(beverage_selection, cup);
         cup = add_to(water, cup);

         if(milk is wanted) {
             cup = add_to(milk, cup);
         }

         // stir and we're done.
         cup = stir_with(cup, spoon);
         return cup;
    }


So here we'd need to work out what types things are and write a few subroutines 
and we have something that can make tea, coffee or hot chocolate.  :)


> ========================
>
> #include<stdio.h>
>
> void multiplynumbers (int, int);
>
> int main (void)
> {
>    multiplynumbers(10,11);
>    return 0;
> }
>
> void multiplynumbers (int a, int b)
> {
>    int total;
>    total = a * b;
>    printf ( "%d times %d equals %d.\n",a,b,total);
> }
>
> =========================
>

Calling functions for the things they do, rather than the things they return, is 
called "relying on their side-effects".  This isn't inherently bad, we rely on 
the side effects when we call printf() all the time (it prints stuff for us, we 
don't really care what it returns (although perhaps we should)).  However, we 
have to be careful to name our functions to give the casual reader of our code a 
hint of what the side effect we're relying on does.  For example, in the code 
above, "multiplynumbers" doesn't *sound* like the kind of function that's going 
to print something to the screen.  So if you were to see the main code, and not 
be able to immediately see multiplynumbers() nearby (perhaps it's in another 
file), you might wonder why it's return value was being ignored, because it 
looks like a function that should be *doing* something and thus *returning* 
something.

There is a slightly related issue called "action from a distance" where errors 
(and other interesting conditions) can propagate through your code and cause 
problems in unexpected places.  For example if we open a file, and then pass the 
file handle to a subroutine, without checking that the file opened successfully, 
then in the situation when the file did not open successfully, the error will 
come from within our subroutine (which is a distance from where the error 
occurred).  eg:

     int main(void) {
         ...
         FILE *fh;
         fh = fopen("somefile.txt", "r"); // fopen returns a false value on 
failure, we really should check it

         // but we're not going to
         somefunction(fh);
         ...
     }

     // then later
     int somefunction(FILE *fh) {
         char * string;

         string = fgets(fh);  // This is where the error happens if we didn't 
succeed in opening the file
         ...
     }

It helps to be aware of these issues.

     J


More information about the Courses mailing list