Archive-name: c-notes/Lesson06.txt

                                  Lesson 6

          Libraries, why we have them, and how to make and use them.

  In order to simplify the creation of programs for its customers,
software vendors make available one or more libraries of functions
which have general application.

  When you write a program in 'C' much, if not most, of the "hard work"
is done by explicitly called functions, which you call by simply writing
their names in your program script. Unfortunately there is a little bit
more to it than that. If the function returns a value which is other
than of type int, you have to tell the compiler the type of the returned
value. An example will, I hope, make things clear.

  Let's suppose that you have been given the task of, making things
stupidly simplistic for a book example, sorting a list of names into
alphabetical order. ( Yes, I do know this can, and should be done in just 
one line of shell script! However that's another story for another day. )

  You look diligently through the Programmer Reference Manual, discover
that the prose is almost opaque, and find a couple of interesting looking
routines called strcmp, and qsort. You decide to use these library
functions. Now for just a moment lets consider the ins and outs of what,
in effect, you have just done. You have just asked a member of the team
of programmers who created the library to join you, by proxy as it were,
in creating your masterpiece. A useful concept, which has been in use
almost since the start of electronic computing.

  To re-focus the mind on the task at hand; let's look in the Reference Manual
at the page for qsort(3C) - The 3C in parenthesis is the cryptic code which
is the unix apology for a reference to section 3C in the Manual! So find 
section 3C and look up qsort. Now have a look at the SYNOPSIS, and notice
that there is no mention of a header file to #include, and also notice that
qsort returns a void, not an int. This means that there is no header file
/usr/include/qsort.h ( for my version of unix - system V Release 3.2.2 -
anyway ) and you have to declare qsort yourself as an external function. 
Also turn to the page string(3C) in the fine manual. Notice that the 
SYNOPSIS here includes the line #include <string.h> so you have to put 
it in your program text. Once more an example to make it all clear.

 /* -------------------------------cut here--------------------------------- */

#ident "qsort-demo2.c - sorting an array of pointers."
#if defined(__STDC__)
#include <stdlib.h>          /* includes - sys/stdtypes.h    */
#endif

#include <ctype.h>           /* includes - nothing           */
#include <stdio.h>           /* includes - nothing           */
#include <string.h>          /* includes - sys/stdtypes.h    */
#include <assert.h>          /* includes - nothing           */

/*
** This makes an array of pointers to the strings.
** The is no need to dimension the array as the compiler does it for us
** by counting up the number of initialisers.
*/

static char *namev[] =
{
   "John Nagle", "Colin Douthwaite", "Ian Lance Taylor", "Brian J. Murrell",
   "Pete", "Geoff Mccaughan", "David Liebert", "Bill Baucum",
   "Victor Volkman", "Chay R Harley", "Dan Romanchik", "Larry Kollar",
   "Gaston Ormazabal", "Arijit Chandra", "Kenneth Mark Hopkinson",
   "Kerr Hatrick", "Tim Love", "Robert M. Juranitch", "Jeffrey Micke",
   "Duong Quoc", "Jagadesh Vasudevamurthy"
};

#define NUMBER_OF_NAMES (sizeof(namev)/sizeof(namev[0]))

/*
** Wrapper function to use the library string comparison function.
** The pointers to pointers which point at the character strings are
** dereferenced to be pointers to the strings themselves.
*/

int CompareStrings (s1, s2)
char **s1, **s2;
{
   return strcmp(*s1, *s2);
}

/*
** Wrapper function to sort by the last name instead of the whole string.
** The pointer to pointer (etc) issue is the same as the above.
** This shows how a more complicated comparison is possible, because
** the comparison function is called from within the qsort function.
*/

int CompareNames ( s1, s2 )
char **s1, **s2;
{
   char *last1, *last2;

   if ((last1 = strrchr(*s1, ' ')) == NULL)
      last1 = *s1;
   else
      last1++;
   
   if ((last2 = strrchr(*s2, ' ')) == NULL)
      last2 = *s2;
   else
      last2++;
   
   return strcmp(last1, last2);
}

void wait_for_key()
{
   char ch;

   printf ( "Press the RETURN key to continue." );
   fflush ( stdout );
   ( void ) getchar ();
   }

int main ()
{
   int i;

   fprintf(stdout, "\n\nOriginal:\n\n");
   for (i=0; i<NUMBER_OF_NAMES; i++) fprintf(stdout, "%s\n", namev[i]);

   wait_for_key();

   qsort((char *)namev, NUMBER_OF_NAMES, sizeof(char *), CompareStrings);
   fprintf(stdout, "\n\nSorted:\n\n");
   for (i=0; i<NUMBER_OF_NAMES; i++) fprintf(stdout, "%s\n", namev[i]);

   wait_for_key();

   qsort((char *)namev, NUMBER_OF_NAMES, sizeof(char *), CompareNames);
   fprintf(stdout, "\n\nSorted by last name:\n\n");
   for (i=0; i<NUMBER_OF_NAMES; i++) fprintf(stdout, "%s\n", namev[i]);

   return 0;
}

 /* -------------------------------cut here--------------------------------- */

  Note very well:-
  
  I wanted 21 short character strings for the data items for the demo
to sort. So grep, uniq, cut, tail, and finally a tiny bit of vi fished
eminently suitable strings out of "mail.received". If your name is not
on the list, well I'm sorry, but the world is not a fair place!

  So that's how you use library routines. I chose qsort because it is
simple to use, and shows off a feature of 'C' well, that's the ability
to use a name of a function as a pointer and then execute that function
from within the called function. It's strcmp in this case. A quick look
at the compiler output is instructive.

  There is just one more point to notice about using function libraries.
The 'C' compilation system will load functions from the library /lib/libc.a
as a default. All others have to be indicated to the linking loader by a
switch on the shell interactive command line. 

$ cc -o prog prog.c -L /usr/local/lib -lgdbm -lmalloc

  You might use this command line to compile and link a program which
uses both the GNU gdbm data-base manager library, which is installed in
the directory /usr/local/lib, and the enhanced malloc library. Now, there
hangs a tale! I remember having to compile a program suit off Usenet and
it just would not work properly. No error messages, no warnings, no
missing linking-loader symbols. It just "died" when I tried to run it.
After many, many hours of total frustration, I thought that I would try
linking in the enhanced malloc library. Presto! It worked.


  Note very well.

  A common misconception is the notion that having a #include <whatever.h>
line in the source text will automagically tell the linking loader to
get the functions from the appropriate library. Remove this erroneous
notion from your mind. It won't. The -lwhatever flag on the shell command
line which initiates execution of "cc" or "ld" is the only way to tell the
loader where to look for the required library.

  Acknowledgement:- 

  mundrane@jove.rutgers.edu (Michael Mundrane)

  has provided the enhanced version of the demonstration program.


Copyright notice:-

(c) 1993 Christopher Sawtell.

I assert the right to be known as the author, and owner of the
intellectual property rights of all the files in this material,
except for the quoted examples which have their individual
copyright notices. Permission is granted for onward copying,
storage, but not modification, of this course in electronic data
retrieval systems, and its use for personal study only, provided
all the copyright notices are left in the text and are printed
in full on any subsequent paper reproduction.

In other words you may pass it around to your friends and print it
out in full on paper, but you may not steal my text and pretend
you wrote it, change the text in any way, or print it as a bound book.

--
 +----------------------------------------------------------------------+
 | NAME   Christopher Sawtell                                           |
 | SMAIL  215 Ollivier's Road, Linwood, Christchurch, 8001. New Zealand.|
 | EMAIL  chris@gerty.equinox.gen.nz                                    |
 | PHONE  +64-3-389-3200   ( gmt +13 - your discretion is requested )   |
 +----------------------------------------------------------------------+
