[Courses] C Programming For Absolute Beginners, Lesson 6: Moar Functions, Local, Static, Global Variables

Simon Valiquette v.simon at ieee.org
Fri Apr 13 05:58:44 UTC 2012


Carla Schroder wrote:
> LOL! This is awesomely clever .
> 
> I got some compiler warnings; I'm traveling so I'll give it a closer look when 
> I can. Perchance someone can figure out what is causing these warnings:
> 
> $ gcc -Wall -o ~/bin/take-cake take-cake.c
> take-cake.c: In function ‘main’:
> take-cake.c:37:9: warning: implicit declaration of function ‘AnnandFran_take’ 
> [-Wimplicit-function-declaration]


This is caused by a typo.  On line 9, this function is declared with an 
's', but defined without the final 's':

AnnandFran_takes()

On line 37, gcc found that you called the unknown function 
AnnandFran_take() --- whitout an 's'.

Now, gcc is smart enough to realize that the function AnnandFran_take() is 
defined latter in the same file and still compile, but not all compilers 
are that smart.  If I remember, cc on Solaris used to refuse to compile 
such code.


Requiring that a function be defined or at least declared before it is 
used simplified the development of the first C compilers, reduced 
compilation time and helped to save memory during compilation (which was 
very scarce and very expensive 40 years ago).  There is other practicals 
reasons, but it is not useful to elaborate too much at this point.


I'll just notice that you could also have implemented all of your 
functions first and put the main() function only at the end of the 
program.  If you do that, you can usually avoid the need to declare them 
first.  But both approaches are equally valid.

That said, in the special case were function A use B(), and function B use 
A(), you will have no choice to declare those functions before defining them.


> take-cake.c:63:17: warning: unused variable ‘e’ [-Wunused-variable]
> take-cake.c:62:17: warning: unused variable ‘d’ [-Wunused-variable]
> take-cake.c:61:17: warning: unused variable ‘c’ [-Wunused-variable]
> take-cake.c:60:17: warning: unused variable ‘b’ [-Wunused-variable]
> take-cake.c:59:17: warning: unused variable ‘a’ [-Wunused-variable]


55   else if (piecesleft == 0)
56         {
57             printf ( "There are no pieces left.  Done!\n\n");
58             lastround = 1;
59             int a = Dan_takes (piecesleft, lastround);
60             int b = Mike_takes (piecesleft, lastround);
61             int c = Marcie_takes (piecesleft, round, lastround);
62             int d = Fred_takes (piecesleft, lastround);
63             int e = AnnandFran_take (round, lastround);
64         }
65         else
66         {
67             printf ( "There are %d pieces left.\n\n",piecesleft);
68         }


You declared for a second time the variables 'a' to 'e', certainly not 
what you wanted.

Fortunately, you never used them as otherwise you could have needed some 
time to figure out what was going on because the compiler wouldn't have 
generated any warning at all.

That said, if you don't care about the return value, you can just discard 
them like that:

59             Dan_takes       (piecesleft, lastround);
60             Mike_takes      (piecesleft, lastround);
61             Marcie_takes    (piecesleft, round, lastround);
62             Fred_takes      (piecesleft, lastround);
63             AnnandFran_take (round,      lastround);
64       }



In this case, to avoid that kind of cut&paste mistake and improve 
readability, I suggest you to declare all the local variables at once like 
this:

int a, b, c, d, e;


I also noticed this:

   piecesleft = (piecesleft -a);

Instead of doing that, you could also use the following shortcut:

piecesleft -= a;

It does exactly the same, but  is more readable.  There is many similar 
operators, all under the form of +=, *=, /=, &=, |=, etc.

So that gives you something like this:


  28    while (piecesleft > 0)
  29     {
  30         int a, b, c, d, e;
  31         a = Dan_takes (piecesleft, lastround);
  32         piecesleft -= a;
  33         b = Mike_takes (piecesleft, lastround);
  34         piecesleft -= b;
  35         c = Marcie_takes (piecesleft, round, lastround);
  36         piecesleft -= c;
  37         d = Fred_takes (piecesleft, lastround);
  38         piecesleft -= d;
  39         e = AnnandFran_take (round, lastround);
  40         piecesleft -= e;
  41


Note that If you didn't needed to keep the 'a' to 'e' values, you could 
even have written directly:

piecesleft -= Dan_takes       (piecesleft, lastround);
piecesleft -= Mike_takes      (piecesleft, lastround);
piecesleft -= Marcie_takes    (piecesleft, lastround);
piecesleft -= Fred_takes      (piecesleft, lastround);
piecesleft -= AnnandFran_take (round,      lastround);


> take-cake.c:71:1: warning: control reaches end of non-void function [-Wreturn-type]
> 

Line 71 is the last line of the main() function.


As you know, main() is supposed to return an integer and you forgot it. 
For now, just returning 0 at the end of the program is fine.


Ideally, you should check that the scanf() function returns 1 (since you 
asked it to read exactly one value) and if it is not the case, check the 
special variable "errno" and possibly terminate the program with a 
non-zero value and a useful error message (or try to recover from the error).


Proper error management is worth a full lesson in itself, so I'll stop 
here before Carla slap my fingers for explaining too much too early.


> But it runs:
> 

I first tough there was a bug with 1 piece of cake because it took 2 
rounds, but then realized that Fran and Ann pass on first round and that 
they are also the only one to take the last piece.

It is also nice that you cared about proper grammar for the last piece.



SOME ICING
==========

You may like to know that C99 defined a boolean datatype called bool.  You 
simply use it that way:

#include <stdbool.h>	// Very important
bool bLastRound = false;

and then you can now do things like this:

if (bLast)
     {
         printf ("Mike's total pieces:\t\t%d\n", total);
     }

However, you have to avoid it if backward compatibility to earlier 
standards is necessary.


very long printf
----------------

To improve the readability of a very long printf or puts, you can use the 
following tricks:

  puts ("  The party is over and your last 6 guests are leaving.\n"
        " There is leftover cake and you would like them to take it with 
them,\n"
        " so you will pass it around -- as many times as it takes --\n"
        " and ask them each to take some.\n"
        " Dan and Mike have roommates, so as long as there are more than 6 
pieces\n"
        " on any given round, they will each take 2; \n"
        " otherwise, they will take 1.\n"
        "  Marcie will take 1 piece but she has a ride to catch, so she 
will\n"
        " only stay for the first round.\n"
        " Fred will take 1 piece on any round.\n"
        " Fran and Ann are dieting, so they will pass on the first round,\n"
        " but give in and split a piece between them on each subsequent 
round.\n"
        " Everyone is too polite to take the last piece, except for Fran 
and Ann,\n"
        " who will take it and split it.\n"
        "Let's see how many rounds it takes to unload all the cake.\n"
        );


Much better and easier to read and edit, isn't it?  You could also have 
used many puts statements, because speed don't matters at all here.


It is still possible to improve the code, but the techniques that cross my 
mind have either not been covered yet, or goes way, way beyond a beginner 
course (like using object oriented programming in C).


So just fix the small mistakes and then I think that overall it will be a 
good job.


Ok, now I am hungry but there is no cake left... :o(


Simon Valiquette

-- 
A computer without COBOL and Fortran is like a piece of chocolate cake
without ketchup and mustard.




More information about the Courses mailing list