Selectorweb.com New York
home > C Email
C programming language Other pages
intro
books
tutorials
misc
C-tutorial part1
C-tutorial part2
C-tutorial part3
C-tutorial part4
C-tutorial part5
C++
C++ tutorial #1
C++ tutorial #2
intro home - top of the page - email
Dennis Richie shaking hands with President Clinton
the day he received the National Medal of Technology for creating C.
� youtube video - Ken Thomson and Dennis Riche receiving the award - 1998

• http://directory.google.com/Top/Computers/Programming/Languages/C/ - google directory of links
� http://en.wikipedia.org/wiki/C_(programming_language)
� www.ioccc.org - The International Obfuscated C Code Contest
� www.ioccc.org/years.html - Previous IOCCC Winners
� http://cm.bell-labs.com/cm/cs/who/dmr/primevalC.html - Very early C compilers and language

books home - top of the page - email

Books:
 - C Programming Language - by Brian W. Kernighan, Dennis Ritchie (1978 - 1st, 1983, 1988 - 2nd ed.)
 - The C Answer Book (2nd Edition) - by Clovis Tondo,  Scott Gimpel, Brian Kernighan (1988)
 - C: A Reference Manual (5th Edition)- by: Samuel P., III Harbison, Guy L., Jr. Steele (2002)
 - TheUNIX Programming Environment - by Brian W. Kernighan, Rob Pike (1984)

tutorials home - top of the page - email

� http://www.cyberdiem.com/vin/learn.html - C / C++ tutorial
� http://www.kulichki.com/moshkow/CTOTOR/ - Moshkov's library - programming, C/C++/Unix


 
misc home - top of the page - email

• http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html - Calling C++ from C and vice versa
� www.parasoft.com/products/insure/index.htm - Insure++ Automatic Runtime Error Detection
� www.progsource.com/c_windows.html - The Programmer's Source - Windows Development C Resources
� /www.lysator.liu.se/c/c-www.html - Other Sources
� www2.iro.umontreal.ca/~ratib/code/ - Codepage 2.3 - Subject index of C programming resources on the WWW
� www.cprogramming.com - Programming in C and C++ - to help beginner 'coders'
� www.europa.com/~viper - Viper's C/C++ Web Page - Network,windows,graphics,sound
� www.programmersheaven.com - Programmers Heaven - C / C++ Zone
� www.nerdworld.com/nw93.html - C Programming Resources From Nerd World Media
� www.cast.msstate.edu/~billy/c-prog.html - C Programming Resources
� www.redrival.com/bigt - Big T's C/C++ Resource Center.
� www.prineas.com/Links/C/index.html - Essential C/C++ Links
� www.strangecreations.com/library/c/index.htm - Virtual Library - C and C++
� www.infopoll.com/infopoll/surveys/s4598.htm - Borland C++ Survey
� www.geocities.com/SiliconValley/Vista/7336/robcstf.htm - Robin's C Programming
� www.softpanorama.org/Lang/c.shtml - Softpanorama Universitys Annotated C Webliography
� www.angelfire.com/tx2/cplus/index.html - The C/C++ webpage for all levels
� http://members.tripod.com/codist - The Codist - a survey of source code comprehension tools
� www.tek-tips.com/gthreadminder.cfm/lev2/4/lev3/32/pid/205 - C Language forum at Tek-Tips - forums and mutual help system for computer professionals.
� ttp://jvcpp.8m.com - The All-New C Programmer's Paradise (also C++ and JavaScript)
. � www.geocities.com/SiliconValley/Haven/5601 - C N Stuff Home Page - Links, code examples and lots of other stuff.
� www.qb45.com/c - Future Software - files, links, tutorials, message board; all automatically updated by visitors.
� http://homestead.juno.com/hawraned/index.html - Two Bit Software - Translates C software to Java, Applesoft BASIC software to ANSI C with extensions.
� www.allexperts.com/getExpert.asp?Category=1587 - Allexperts C Programming Q&A - Volunteer experts answer your detailed one-on-one questions.
� www.angelfire.com/sc/electron - The C Coders Home Page - A collection of resources including support for unix/dos systems,win32,sockets, and game programming.
� http://lclint.cs.virginia.edu - LCLint - Tool for statically checking C programs, GPL.
� www.flamingolingo.com/programming.c/ - Programming.c Directory of programming sites (C, C++, Java, COBOL, Basic, COM, Pascal, etc.)
� www.genitor.com/resources - Genitor Corporation Developer Resources
� http://hermetic.nofadz.com/cfunlib.htm - C/C++ Programming and C function libraries for calendar date conversion and multidimensional dynamic array allocation; links.
� http://anubis.dkuug.dk/JTC1/SC22/WG14 - Home of the C standard group
� http://www.cs.wisc.edu/csl/doc/info/unix-software/programming/debuggers/ - debuggers
dbx is the Berkeley UNIX symbolic debugger. It is available on Solaris and some other Unix-es. Not supported on Linux. Convenient primarily because it is not X-Windows based. Installed Location: /usr/bin/dbx .  Running the code: Compile and link your code with the -g flag to incorporate debugging information. Then, run your code under the debugger.  For example, dbx a.out .  Core dumps can also be probed using dbx to determine the cause of the crash.
gdb is the standard GNU debugger. Although originally designed to operate with the GNU C compiler (gcc), gdb can be used to debug programs compiled from a variety of different compilers, including vendor-supplied C and Fortran compilers on several platforms (if compiled with debugger symbol information). Emacs includes a GDB mode for simultaneously debugging and editing programs.
ddd is a wrapper around the GDB, DBX, or XDB debuggers. In addition to the command-line interface of the dependent debugger, DDD provides a common graphical user interface to support debugging tasks. The DDD graphical data display allows for interactive exploration of data structures.


 
C-tutorial part 1 home - top of the page - email

The 'C' programming language was designed and developed by Brian Kernighan, and Dennis Ritchie at The Bell Research Labs. It was first implemented in assembler on a Digital Equipment Corporation PDP-7. Once a simple assembler version was working it was possible to rewrite the compiler in 'C' itself. This was done in short order and therefore as soon as the PDP-11 was introduced by DEC it was only necessary to change the code generator section of the compiler and the new machine had a compiler in just a few weeks. 'C' was then used to implement the UNIX o/s. This means, that a complete UNIX can be ported to a new machine in literally just a few months by a small team of competent programmers.

Create file hello.c:
#include <stdio.h>

char *format = "%s",
      *hello = "Hello World...\n";

main() {
  printf ( format, hello );
}

Now type:
cc -o hello hello.c
Now type:
hello
The computer will print
Hello World...

Although we did everything with one command, in fact the compilation process consisted of several stages (pre-processing, compiling, assembling, linking). We can go through them step-by-step:
Pre-processor - replaces the line  #include <stdio.h> with the file stdio.h (standard input/output headers) from the include files library (from directory /usr/include/). To see just this step - use the following commands:
cc -E hello.c
view hello.i
You will see that a number of lines of text have been added at the front of the hello.c program from the file called /usr/include/stdio.h :
view /usr/include/stdio.h
Next stage  - compile into an assembler code program. Use the following commands
cc -S hello.c
view hello.s
You will see some recognizable symbols and lots of assembler code.  To finally turn this into machine instructions use the following command:
cc -g -c hello.s
view hello.o
This new file with suffix ".o" is called the object file. It contains the machine instructions corresponding exactly to the mnemonic codes
in the .s file. If you wish you can look at these machine codes using one of the commands available to examine object files.
dis -L -t .data hello.o >hello.dis
The next stage is "linking" this binary code with libraries - use the command:
cc -o hello hello.o

The single command we use to compile was

cc -o hello hello.c

The word after the -o option is the name of the executable file (default - "a.out"). The source file MUST have the ".c" extension.

Note: the compiler name may be bcc (Borland), gcc (GNU), CC, or something else.
Note: 'C' compilers have option to optimize (for speed or memory usage).
Note: here are some common compielr options:
-c - compile only (do not link)
-S - stop after making assempler code
-E - stop after preprocessing stage
-o file - define the output file
-v - verbouse (print commands executed on STDERR, also print compiler version, etc.)
-l - to specify libraries to link
-L - to specify directories to search for libraries
-I - to specify directories to search for include files
-static - tells compiler to include shared libraries into the executable (thus making it larger) - even though the libraries are available (usually in /lib) and can be linked dynamically during execution (on systems that support dynamic linking)
-shared - produce a shared object which can then be linked with other objects  to  form an executable.  Only a few systems support this option.
-symbolic - bind  references to global symbols when building a shared object, warn about any unresolved references - only if supported.
-On, where n=0,1,2,or 3 - sets level of optimization
-b machine - to specify the type of machine for which to compile
-V version - to specify which version of compiler to run
there are many more options (100s) - see man pages for the compiler.

=============================================================================================

All information (code or data) is stored in a computer in binary formats.

Data types:

Character:
#include <stdio.h>
main() {
  char a;
  unsigned char b;

  a = b = 128;
  a >>= 1;
  b >>= 1;
  printf ( "\nYour computer has %ssigned char.\n\n", a == b ? "un" : "" );
}

compile and run it - and it will print:
   Your computer has unsigned char.

Integers:
  int i;
  short int smaller_number;  /* usually 2 bytes */
  long int big_number;   /* usually 4 bytes */

Real Numbers:

  float length_of_water_line;     /* in meters */
  double displacement;            /* in grammes */

file /usr/include/values.h defines some usefule values:

  MAXFLOAT      3.40282346638528860e+38
  MINFLOAT      1.40129846432481707e-45
  MAXDOUBLE     1.79769313486231470e+308
  MINDOUBLE     4.94065645841246544e-324

Practically,  float - 6, double - 12  significant digits.

Signed and unsigned prefixes.
(for both char and integer types the declaration can be preceded by the word "unsigned").

arrays:
  char client_surname[31];

  This declaration reserves storage for a string of 30 characters plus the NULL character of value zero which terminates the string.

Structures:
 
/* Define the template for 'ship' structure type: */:
struct ship {
  char name[30];
  double displacement;                           /* in grammes */
  float length_of_water_line;                    /* in meters */
  unsigned short int number_of_passengers;
  unsigned short int number_of_crew;
};

/* Now we can create the structure in memory: */
struct ship cunarder;
/* Now we can assign values: */
  cunarder.displacement = 97500000000.0;
  cunarder.length_of_water_line = 750.0
  cunarder.number_of_passengers = 3575;
  cunarder.number_of_crew = 4592;

Here is a short test program:
#include <stdio.h>

struct ship
{
  char name[31];
  double displacement;                              /* in grammes */
  float length_of_water_line;                       /* in meters */
  unsigned short int number_of_passengers;
  unsigned short int number_of_crew;
  };

char *format = "\
Name of Vessel: %-30s\n\
  Displacement: %13.3f\n\
    Water Line: %5.1f\n\
    Passengers: %4d\n\
          Crew: %4d\n\n";

main()
{
  struct ship cunarder;

  cunarder.name = "Queen Mary";                  /* This is the bad line. */
  strcpy ( cunarder.name, "Queen Mary" );      /* The corrected line */
  cunarder.displacement = 97500000000.0;
  cunarder.length_of_water_line = 750.0
  cunarder.number_of_passengers = 3575;
  cunarder.number_of_crew = 4592;

  printf ( format,
           cunarder.name,
                 cunarder.displacement,
           cunarder.length_of_water_line,
           cunarder.number_of_passengers,
           cunarder.number_of_crew
           );
  }

Now let's compile and execute it:
$ cc demo1a.c
$ a.out

Name of Vessel: Queen Mary
  Displacement: 97500000000.0 grammes
    Water Line: 750.0 metres
    Passengers: 3575
          Crew: 4592
 

Three special allocation methods, 'static' and 'register', and 'const':
'static' - will be placed in the main storage to live all the execution time.
'register' - tells compiler that you will be using this a lot - for optimal storage
'comst' - means constant

Pointers.
pointer - a variable which contain the address of a data element.

char c;  /* normal variable */
char *ch_p; /* pointer to a char variable */

Unary operator '&' gives the address of the variable:

ch_p = &c;

Input/output:
"printf" - print formatted output
"scanf" - read data into the program

On unix use manpages to read the options.
man printf
man scanf
#include <stdio.h>

int printf ( format [ , arg ] ... )
char *format;

int fprintf ( stream, format [ , arg ] ... )
FILE *stream;
char *format;

int sprintf ( s, format [ , arg ] ... )
char *s, *format;

Test program:
#include <stdio.h>

char *verse[] = {
  "On top of the Crumpetty Tree",
  "The Quangle Wangle sat,",
  "But his face you could not see,",
  "On account of his Beaver Hat.",
  "For his Hat was a hundred and two feet wide.",
  "With ribbons and bibbons on every side,",
  "And bells, and buttons, and loops, and lace,",
  "So that nobody ever could see the face",
  "Of the Quangle Wangle Quee.",
  NULL
  };

main() {
        char **ch_pp;

        /* This will print the data left justified. */

        for ( ch_pp = verse; *ch_pp; ch_pp++ ) printf ( "%s\n", *ch_pp );
        printf( "\n" );

        /*  This will print the data right justified.  */

        for ( ch_pp = verse; *ch_pp; ch_pp++ ) 
           printf ( "%79s\n", *ch_pp );
        printf( "\n" );

        /* This will centre the data.  */

        for ( ch_pp = verse; *ch_pp; ch_pp++ )  {
                int length;
                char format[10];

                length = 40 + strlen ( *ch_pp ) / 2; 
                sprintf ( format, "%%%ds\n", length ); 
                printf ( format, *ch_pp ); 
                } 
        printf( "\n" );
        }

 

scanf() - family of functions used to input from the outside world, note that  arguments to the function are all POINTERS. The format string has to be passed in to the function using a pointer, simply because this is the way 'C' passes strings.

 
C-tutorial part 2 home - top of the page - email

Arrays and Pointers.
  char name[30];                   /* An array of 30 signed characters. */
  char *strings[50];               /* 50 pointers to strings. */
  unsigned long int *(*func)()[20];/* An array of pointers to functions which */
                                                  /* return pointers to unsigned long ints. */
  struct ship *vessel_p;
  struct ship fleet[5];     /* This allocates enough storage for 5 ships' info.*/

  vessel_p = fleet; /*point at the first vessel in the fleet.*/
  vessel_p++;             /* point a the next ship in the fleet array. */
  vessel_p = fleet + 3;
  i = vessel_p - fleet; /* the index of the ship in the fleet at which we are pointing */
  d = vessel_p - another_vessel_p; /* This gives the separation in elements. */

Note: the result of pointer arithmetic is ALWAYS expressed in elements rather than bytes.
/* Using a pointer to reference a structure: */
  vessel_p = fleet;

  vessel_p->name = "Queen Mary";
  vessel_p->displacement = 97500000000.0;
  vessel_p->length_of_water_line = 750.0
  vessel_p->number_of_passengers = 3575;
  vessel_p->number_of_crew = 4592;

   Remember:
       It's a "." when accessing a struct which is in storage declared in the program.
       It's a "->" when accessing a struct at which a pointer is pointing.

  Initialisation of arrays.
  char *qbf = "The quick brown fox jumped over the lazy dogs back";

  int tic_tac_toe[3][3] =
  {
    { 1, 2, 3 },
    { 4, 5, 6 },
    { 7, 8, 9 }
    };

  struct ship fleet[2] =
  {
    { "Queen Elizabeth",  97500000000.0, 750.0, 3575, 4592 },
    {      "Queen Mary", 115000000000.0, 875.0, 4500, 5500 }
    };

char *verse[] =    /* array of pointers */
{
  "On top of the Crumpetty Tree",
  "The Quangle Wangle sat,",
  "But his face you could not see,",
  "On account of his Beaver Hat.",
  "For his Hat was a hundred and two feet wide.",
  "With ribbons and bibbons on every side,",
  "And bells, and buttons, and loops, and lace,",
  "So that nobody ever could see the face",
  "Of the Quangle Wangle Quee."
  NULL     /* this is to tell the compiler that this is the end */
  };
 

   The other way is to calculate the size of the table by using the sizeof  operator - Note that although use of sizeof looks like a function call  it is in fact an intrinsic operator of the language. The result is available at compile time. So one can say:-

        #define SIZE_OF_VERSE sizeof verse

enum:
enum spectrum { red, orange, yellow, green, blue, indigo, violet } colour;

In this construct the first symbol is given the value of 0 and for each following symbol the value is incremented.
It is however possible to assign specific values to the symbols like this:

  enum tub
  { anorexic = 65,
    slim = 70,
    normal = 80,
    fat = 95,
    obese = 135
    };

Here is another trivial program which demonstrates the use of enum and a pre-initialised array.
#include <stdio.h>

enum spectrum { red, orange, yellow, green, blue, indigo, violet } colour;

char *rainbow[] = { "red", "orange", "yellow", "green",
                    "blue", "indigo", "violet" };

main() {
  for ( colour = red; colour <= violet; colour++ ) {
    printf ( "%s ", rainbow[colour]);
    }
  printf ( "\n" );
}

   The output of which is:

red orange yellow green blue indigo violet

Yet another example program:
char *ident = "@(#) tellme.c - An example of using a pointer to a function.";

#include <stdio.h>
#include <math.h>
#include <sys/errno.h>

/*
These declarations are not in fact needed as they are all declared extern in
math.h. However if you were to use routines which are not in a library and
therefore not declared in a '.h' file you should declare them. Remember you
MUST declare external routines which return a type other than the int type.

extern double  sin ();
extern double  cos ();
extern double  tan ();
extern double atof ();
*/

struct table_entry
{
  char *name;                        /* The address of the character string. */
  double (*function)();   /* The address of the entry point of the function. */
  };

typedef struct table_entry TABLE;

double help ( tp )
TABLE *tp;
{ printf ( "Choose one of these functions:- " );
  fflush ( stdout );
  for ( ; tp -> name; tp++ ) printf ( "%s ", tp -> name );
  printf ( "\nRemember the input is expressed in Radians\n" );
  exit ( 0 );
  return ( 0.0 );  /* Needed to keep some nit-picking dumb compilers happy! */
  }

/*
 * This is the array of pointers to the strings and function entry points.
 * Is is initialised at linking time. You may add as many functions as you
 * like in here PROVIDED you declare them to be extern, either in some .h
 * file or explicitly.
 */

TABLE interpretation_table [ ] =
{
  { "sin",  sin  },
  { "tan",  tan  },
  { "cos",  cos  },
  { "help", help },
  {  NULL,  NULL }               /* To flag the end of the table. */
  };

char *output_format = { "\n %s %s = %g\n" };
extern int errno;
extern void perror();

main( argc, argv )
int argc;
char **argv;
{
  TABLE *tp;
  double x, answer;

  if ( argc > 3 )
  {
    errno = E2BIG;
    perror ( "tellme" );
    exit ( -1 );
    }

  for (;;)                  /* This is the way to set up a continuous loop. */
  {
    for ( tp = interpretation_table;
          ( tp -> name && strcmp ( tp -> name, argv[1] ));
          tp++
    )  ;                      /* Note use of empty for loop to position tp. */

    if ( tp -> function == help ) (*tp -> function )( interpretation_table );
    if ( tp -> name == NULL )
    {
      printf ( "Function %s not implemented yet\n", argv[1] );
      exit ( 1 );
      }
    break;                     /* Leave the loop. */
    }

  x = atof ( argv[2] );        /* Convert the character string to a double. */
  answer = ( *tp -> function )( x );/* Execute the desired function.        */
  printf ( output_format,      /* Pointer to printf()'s format string.      */
           argv[1],            /* Pointer to the name of the function.      */
           argv[2],            /* Pointer to the input number ascii string. */
           answer              /* Value ( in double floating point binary ) */
           );
  }

Precedence.
        First up come what are called the primary-expression operators:

                ()    Function.
                []    Array.
                .     struct member ( variable ).
                ->    struct member ( pointer ).

         The unary operators:

                *     Indirection via a Pointer.
                &     Address of Variable.
                -     Arithmetic Negative.
                !     Logical Negation or Not.
                ~     Bit-wise One's Complement.
                ++    Increment.
                --    Decrement.
                sizeof  Which is self explanitary.

        Now the binary operators:

   Arithmetic Operators.

                *     Multiply.                                       My
                /     Divide.                                         Dear
                %     Modulo, or Remainder of Integer Division.
                +     Addition.                                       Aunt
                -     Subtraction.                                    Sally

         The Shifting Operators.

                >>    Bit-wise Shift to the Right.
                <<    Bit-wise Shift to the Left.

   Logical Relation Operators.

                <     Less Than.
                >     Greater Than.
                <=    Less Than or Equal.
                >=    Greater Than or Equal.
               ==    Equal.
                !=    Not Equal.

         Bit-wise Boolean Operators.

                &     Bit-wise And.
                ^     Bit-wise Exclusive-Or (XOR).
                |     Bit-wise Or.

         The Logical Operators.

                &&    Logical And.
                ||    Logical Or.

   The Assignment Operators. ( They all have the same priority. )

                =     The normal assignment operator.

         The Self-referencing Assignment Operators.

                +=
                -=
                *=
                /=
                %=
                >>=
                <<=
                &=
    ^=
                |=

        a = 8;
        a += 2;     /* The result is 10 */

        a = 7;
        a ^= 2;    /* Now a is 5 */
        a ^= 2;    /*  and back to 7. */

Note:  with old compiler you may have problems that can be cured by putting spaces on either side of the '=' sign or bracketing the unary minus to the operand.
        a=(-2);
        a = -2;

Note:   >>  If you shift a signed integer to the right when the sign bit is set then in all probability the sign will be extended.
 
#include <stdio.h>

#define WORD_SIZE ( sizeof ( INTEGER int ) * 8 )
#define NIBBLE_SIZE 4
#define NIBBLES_IN_WORD (( WORD_SIZE ) / NIBBLE_SIZE )
#define SIGN_BIT ( 1 << ( WORD_SIZE - 1 ))

char *title[] ={ "       Signed             Unsigned",
             "                 Signed                                 Unsigned"
 };

main () {
        INTEGER int a;
        unsigned INTEGER int b, mask;
        int ab, i, j, bit_counter, line_counter;

        a = b = SIGN_BIT;
        printf ( "%s\n\n", title [ ( WORD_SIZE == 16 ) ? 0 : 1 ] );

        for ( line_counter = 0; line_counter < WORD_SIZE; line_counter++ )  {
                for ( ab = 0; ab < 2; ab++ )   {
                        mask = SIGN_BIT;
                        for ( i = 0; i < NIBBLES_IN_WORD; i++ )   {
                                for ( j = 0; j < NIBBLE_SIZE; j++ )   {
                                        printf ( "%c", ((( ab ) ? b : a ) & mask ) ? '1' : '0' );
                                        mask >>= 1;
                                 }
                                printf ( " " );
                          }
                        printf ( "%s", ( ab ) ? "\n" : " " );
                        if ( ab )    {
                          b >>= 1;
                         } else {
                          a >>= 1;
#if defined(FIX_COMPILER_BUG)
# if (INTEGER == long)
                                a |= SIGN_BIT;    /* This is a work-around for the 3b2 compiler bug. */
# endif
#endif
                         }
                    }
                }
        }

This program may behave differently for short and long integers because compiler issuing a Logical Shift instruction, when it should issue a Arithmetic Shift instruction for signed integers and a Logical Shift instructon for unsigned ones.  Here are the compiler invocation lines.

cc -olong.shifts -DFIX_COMPILER_BUG -DINTEGER=long shifts.c
        and
cc -oshort.shifts -DINTEGER=short shifts.c

Experiment with the "-DFIX_COMPILER_BUG" and see what your compiler does.

 
C-tutorial part 3 home - top of the page - email

The Pre-processor and Header Files.

The pre-processor is activated by a '#' character in column one of the source code. There are several statements vis:

#include

#define
#undef

#if
#else
#endif

#ifdef
#ifndef

#pragma

#include.

Examples:
#include <stdio.h>

#if ( FLAG )
# include "true.h"
#else
# include "false.h"
#endif

If the file name is < .. > - then it comes from /usr/include directory ( or its /sys/  subdirectory ).
If the file name is in " .. " - then it comes from the current working drectory (or you can extend search path using command-line switches).
 
extern struct tm *gmtime(), *localtime();
extern char *ctime(), *asctime();
int  cftime(),  ascftime();
extern void tzset();
extern long timezone, altzone;
extern int daylight;
extern char *tzname[];

hw_uc.h
#define HELLO_MESSAGE "HELLO WORLD...\n";

hello.c
#include <stdio.h>
#include HW_H

#if !defined( HELLO_MESSAGE )
# error "You have forgotten to define the header file name."
#endif

char *format = "%s", 
       *hello = HELLO_MESSAGE;

main() { printf ( format, hello ); }

cc -DHW_H="\"hw_uc.h\"" hello.c

The compiler output is placed, by default, in the file a.out, so to execute it issue the command:
a.out

Which, fairly obviously, produces the output:
HELLO WORLD...

As we are going to generate another version of the program we had better move the executable image file to another file name:
mv a.out hello_uc

Now to produce the other version issue the command line:
cc -DHW_H="\"hw_lc.h\"" hello.c; mv a.out hello_lc; hello_lc

Which compiles the lower-case version of the hello.c program, using this version of the include file:
hw_lc.h
#define HELLO_MESSAGE "Hello World...\n";
and then moves the executable image to a different file and executes it producing this output:
Hello World...

commands included in a shell file:
# @(#) Shell file to do the compilations.

cc -o hello_uc -DHW_H="\"hw_uc.h\"" hello.c
cc -o hello_lc -DHW_H="\"hw_lc.h\"" hello.c

#define
 - to set up macro definitions. See many exampels in /usr/include/sys/file.h .
For example:

#define FAPPEND         0x08
#define min(a, b) ((a<b) ? a : b )

NOTE:
  1) There isn't a space between the last character of the symbol being defined and the opening parenthesis enclosing the arguments, and there MUST NOT BE one.
  2) The code into which the macro is expanded MUST always be enclosed in parentheses and for safety always use parentheses to get the arithmetic right.
  3) Never EVER define a macro, and use it with a side effect. e.g.
  c = min ( a++, b);                              /* DON'T _EVER_ DO THIS!!! */
     Do you think that the value of 'a' will get advanced after the macro is used? Well it WON'T. It gets incremented after the less than test and before the values get assigned! Generate the output from the pre-processor of the code below to see what's going on:
 
#include <stdio.h>
main() { 
  int a,b,c;
  a = 1;
  b = 2;
  c = min ( a++, b);                              /* DON'T _EVER_ DO THIS!!! */
  printf ( "a: %d, b: %d, c: %d\n", a, b, c );
}

  4) You can continue a macro on the next line by putting a \ ( back-slash ) as THE VERY LAST character on the line.
 

#undef:
 - this preprocessor command removes a symbol WHICH IS BEING USED BY THE PRE-PROCESSOR.

#if ( FLAG )
      /* Code in here is sent on to the compiler if FLAG is true. */
#else
      /* Code in here is sent on to the compiler if FLAG is false. */
#endif

You are also allowed to say:
#if defined( FLAG )
#if !defined( FLAG )

Here is an old way of doing the same:
#ifdef   FLAG
#ifndef   FLAG

By convention all pre-processor symbols are in UPPER_CASE.

#pragma
  - used to alter the way in which the compiler works on a block of code, it is implementation dependent.

The "stringizing" operator - will discuss later

 
C-tutorial part 4 home - top of the page - email

Libraries.

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. For example, if you want to use library functions strcmp, and qsort. Let's look manpage for qsort(3C) , where the 3C in parenthesis is the cryptic code which is the unix apology for a reference to section 3C in the Manual.  In SYNOPSIS 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  and you have to declare qsort yourself as an external function. Now look manpage for string(3C) - the SYNOPSIS here includes the line #include <string.h> so you have to put it in your program text.
 
#ident "@(#) qsort-demo.c"
#include <stdio.h>
#include <string.h>
#include <assert.h>

extern void qsort ();
extern int strcmp();        /* Some compilers need this defined, most don't. */

char names[22][25] =                         /* Here are some names to sort. */
{
  "John Nagle", "Colin Douthwaite", "Ian Lance Taylor", "Brian J. Murrell",
  "Pete", "Geoff Mccaughan", "David Liebert", "Operator", "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 ( names ) / sizeof ( names[0] )

main()
{
  int i;

        /*
        ** Print the unsorted names.
        */

        printf ( "The Unsorted Names.\n" );
  for ( i = 0; i < NUMBER_OF_NAMES; i++ ) printf ( "%s\n", names[i] );

        /*
        ** Print a prompt, and wait.
        */

        printf ( "Press RETURN to continue: " );
        fflush ( stdout );
        getchar();

        /*
        ** Now apply qsort to the arrary of character strings.
        */

        qsort (( char * ) names, NUMBER_OF_NAMES, sizeof ( *names ), strcmp );

        assert ( names[0][0] < names[1][0] );  /* Quick check to see it's done
it. */

        /*
        ** Print the sorted names.
        */

        printf ( "The Sorted Names.\n" );
  for ( i = 0; i < NUMBER_OF_NAMES; i++ ) printf ( "%s\n", names[i] );
  }

This example shows also that you can the name of a function (strcmp) to another function (qsort) for execution.

The 'C' compilation system will load library 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. Note that having a #include <whatever.h> line in the source text will NOT automagically tell the linking loader to get the functions from the appropriate library. 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.
 

De-bugging Strategies.

  1) Document what you are going to do before (yes BEFORE) you write any code.
  2) Make sure that you keep each file as small as is sensible.
  3) Always use names for the objects in your program which are fully descriptive.
  4) ALWAYS take great care with the layout of your code. You may consider to always put the opening brace of ALL program structures  on a new line. Also if you put them in the leftmost column for structs, enums, and initialised tables, as well as functions, then the 'find function' keystrokes ( "[[" and "]]" ) in vi will find them as well as the functions themselves. Make sure you have the "showmatch" facility in vi turned on. ( And watch the cursor jump when you enter the right hand brace, bracket, or parenthesis. )
  5) Try as hard as you can to have as few global variables as possible. If you absolutely have to have several globals - then confine the scope of the globals to just the one file by marking the defining declaration "static". This stops the compiler producing a symbol which the linking loader will make available to all the files in your source.
  6) Never EVER put 'magic numbers' in you source code. Always define constants in a header file with #define lines or enum statements.

Here is an example:-
#include <stdio.h>

enum status_input_names
{
  radiator_temperature,
  oil_temperature,
  fuel_pressure,
  energy_output,
  revolutions_per_minute
  };

char *stats[] =
{
  "radiator_temperature",
  "oil_temperature",
  "fuel_pressure",
  "energy_output",
  "revolutions_per_minute"
  };

#define NUMBER_OF_INPUTS ( sizeof ( stats ) / sizeof ( stats[0]))

main()
{
  enum status_input_names name;

  printf ( "Number of Inputs is: %d\n", NUMBER_OF_INPUTS );
  for ( name = radiator_temperature; name < NUMBER_OF_INPUTS; name++)
  {
    printf ( "\n%s", stats[ name ] );
    }
  printf ( "\n\n" );
  }

  Note that as a side effect we have available the meaningful symbols radiator_temperature etc. as indices into the array of status input names and the symbol NUMBER_OF_INPUTS available for use as a terminator in the 'for' loop. This is quite legal because sizeof is a pseudo-function and the value is evaluated at the time of compilation and not when the program is executed. This means that the result of the division in the macro is calculated at the time of compilation and this result is used as a literal in the 'for' loop.

Some advice:
  1) Don't get confused between
   == - the logical equality operator, and
   =   - the assignment to a variable operator.

  2) Make sure that you are aware of the difference between the logical and bit operators.

     &&         This is the logical AND function.
     ||         This is the logical OR function.
                The result is ALWAYS either a 0 or a 1.

     &          This is the bitwise AND function used for masks etc.
                The result is expressed in all the bits of the word.

  3) Similarly to 2 be aware of the difference between the logical complementation and the bitwise one's complement operators.

     !          This is the logical NOT operator.
     ~          This is the bitwise ones complement op.
 

NOTE:  a LOGICAL variable is said to be TRUE when it is non-zero, and FALSE when it is zero.

  4) Make sure that you use an editor which tells you the matching symbol.

  Example 1.
function_type function_name ( a, b )
type a;
type b;
{
  type variable_one, variable_two;

  if ( logical_expression )
  {
    variable_one = A_DEFINED_CONSTANT;
    if ( !return_value = some_function_or_other ( a,
                                                  variable_one,
                                                  &variable_two
                                                  )
         )
    {
      error ( "function_name" );
      exit ( FAILURE );
      }
    else
    {
      return ( return_value + variable_two );
      }
    }    /* End of "if ( logical_expression )" block */
  }    /* End of function */

This layout is easy to do using vi with this initialisation script in either the environment variable EXINIT or the file ${HOME}/.exrc:-

set showmode autoindent autowrite tabstop=2 shiftwidth=2 showmatch wm=1

  Example 2.
void printUandG()
{
  char *format =
"\n\
           User is: %s\n\
          Group is: %s\n\n\
 Effective User is: %s\n\
Effective Group is: %s\n\n";

  ( void ) fprintf ( tty,
                     format,
                     passwd_p->pw_name,
                     group_p->gr_name,
                     epasswd_p->pw_name,
                     egroup_p->gr_name
                     );
  }

Notice how it is possible to split up format statements with a '\' as the last character on the line (not even space may follow it).

 - put the opening brace on a new line,
 - set the tabs to just 2 spaces
 - break long lists of argumants into several lines
/*
  ** Put all the cursor positions to zero.
  */

  for ( i = 0;
        s[i].element_name != ( char *) NULL &&
        s[i].element_value != ( char *) NULL;
        i = ( s[i].dependent_function == NULL )
            ? s[i].next : s[i].dependent_next
        )
  {                              /* Note that it is the brace and NOT the    */
                                 /* "for" which moves the indentation level. */
    s[i].cursor_position = 0;
    }

  /*
  ** Go to start of list and hop over any constants.
  */

    for ( i = edit_mode = current_element = 0;
          s[i].element_value == ( char *) NULL ;
          current_element = i = s[i].next
          ) continue;                               /* Note EMPTY statement. */

  /*
  ** Loop through the elements, stopping at end of table marker,
  ** which is an element with neither a pointer to an element_name nor
  ** one to a element_value.
  */

  while ( s[i].element_name != ( char *) NULL &&
          s[i].element_value != ( char *) NULL
          )
  {
    int c;           /* Varable which holds the character from the keyboard. */

    /*
    **  Et Cetera for many lines.
    */

    }

  Note the commenting style. The lefthand comments provide a general overview of what is happening and the righthand ones a more detailed view. The double stars make a good marker so it is easy to separate the code and the comments at a glance.

  The null statement:

 ";" - a no-operation statement.

 The assert macro:
#ident "@(#) assert-demo.c"

#include <stdio.h>
#include <assert.h>

#define TOP_ROW 10
#define TOP_COL 10

main()
{
  int row, col;

  for ( row = 1; row <= TOP_ROW; row++);
  {
    assert ( row <= TOP_ROW );
    for ( col = 1; col <= TOP_COL; col++ )
    {
      assert ( col <= TOP_COL );
      printf ( "%4d", row * col );
      }
    printf ( "\n" );
    }
  }

Which produces the output:-
Assertion failed:  row <= TOP_ROW , file assert-demo.c, line 15
ABORT instruction (core dumped)

It does this because the varable "row" is incremented to one greater than The value of TOP_ROW.

Note two things:

  1) The sense of the logical condition. The assert is asserted as soon as the result of the logical condition is FALSE. Have a look at the file /usr/include/assert. Where is the ";" being used as an empty program statement?

  2) The unix operating system has dumped out an image of the executing program for examination using a symbolic debugger. Have a play with "sdb" in preparation for the lesson which deals with it in more detail.

 
C-tutorial part 5 home - top of the page - email

Looping structures:
  do ... while ( ... );  /* executed at least once, there is no `until' test at the end of a loop */
  while ( ... ) repeated_statement;  /* if false - then repeated_statement is NEVER executed */

Associated with the looping structures are the control words:
  break;   - allows you to leave a loop in the middle of a block, and
  continue;   - allows you to re-start it from the top.

Finally we must not forget the most common and useful looping construct:
  for ( init; test; increment ) repeated_statement;

Some examples.

  A do loop program.
#ident "@(#) do_demo.c - An example of the do loop"

#include <stdio.h>

main()
{
  char character;

  character = 'a';

  do printf ( "%c", character ); while ( character++ < 'z' );
  printf ( "\n" );
  }

  Fairly obviously it prints:

abcdefghijklmnopqrstuvwxyz

  A while loop example.

#ident "@(#) while_demo.c - An example of the while loop"

#include <stdio.h>

main()
{
  char character;

  character = 'a';

  while ( character <= 'z' ) printf ( "%c", character++ );
  printf ( "\n" );
  }

Its output is exactly the same as the previous example:

abcdefghijklmnopqrstuvwxyz

  In this totally trivial case it is irrelevant which program structure you use, however you should note that in the `do' program structure the repeated statement is always executed at least once.
  A for loop example.

  The `for' looping structure.
#ident "@(#) for_demo.c - An example of the for loop"

#include <stdio.h>

main()
{
  char character;

  for ( character = 'a'; character <= 'z' ; character++ )
  {
    printf ( "%c", character );
    }
  printf ( "\n" );
  }
 

  Surprise, Surprise!

abcdefghijklmnopqrstuvwxyz

  You should be aware that in all the looping program structures, the repeated statement can be a null statement ( either just a `;' or the reserved word `continue;' ). This means that it is possible to - for example - position a pointer, or count up some items of something or other. It isn't particularly easy to think up a trivial little program which demonstrates this concept, however the two `for' loops give some indication of the idea.
#ident "@(#) pointer_demo.c - Pointer operations with the for loop"

#include <stdio.h>

main()
{
  char character, *character_pointer, alphabets [ 53 ];

  for ( character = 'a', character_pointer = alphabets;  /* Start conditions */
        character <= 'z';                                /* Run while true   */
        *character_pointer++ = character++               /* All the work     */
        )TRUE continue;

  for ( character = 'A';  /* character_pointer is at the right place already */
        character <= 'Z';
        *character_pointer++ = character++
        ) continue;

  *character_pointer = (char) '\000'; /* NULL character to terminate string. */

  printf ( "%s\n\n", alphabets );
  }

  Another Surprise!

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
 

switch
if
if ... else
 
#ident "if_demo.c"
#include <stdio.h>
main(argc, argv)
int argc;
char **argv;
{
  if ( argc > 1 ) printf ( "You have initiated execution with arguments."};
  }

  And the if ... else demo.

#ident "if_else_demo.c"
/*
** The Language #define could go in the compiler invocation line if desired.
*/

#define ENGLISH

#include <stdio.h>

/*
** The message and text fragments output by the program.
*/

char *messages[] =
{
#if defined( ENGLISH )
#ident "@(#)ENGLISH Version"
  "\nUsage: if_else_demo <numeric argument 1> <numeric argument 2>\n\n",
  "The first argument is ",
  "the second",
  "equal to ",
  "bigger than ",
  "smaller than "
#endif

#if defined( FRANCAIS )
#ident "@(#)FRENCH Version"

  put the French translation in here so that we are ready to export to
  French speaking Countries. I'd be grateful if a French speaker could
  make the translation for me.

#endif
  };

/*
** Meaningful words defined to constants
*/

#define USAGE 0
#define FIRST 1
#define SECOND 2
#define EQUAL 3
#define BIGGER 4
#define SMALLER 5

#define SUCCESS 0
#define FAILURE 1

/*
** We need this more than once so it can be put in a function.
*/

void usage()
{
  printf ( messages[USAGE]);
  exit ( FAILURE );
  }

/*
** Main program function starts here. ( At the top of a page no less! )
*/

int main ( argc, argv )
int argc;
char **argv;
{
  int message_index;
  double i, j, strtod();
  char *ptr;

  if ( argc != 3 ) usage();                  /* have we been given the right */
                                             /* number of arguments. */
  i = strtod ( argv[1], &ptr);               /* Convert to a double float. */
  if ( ptr == argv[1] ) usage();             /* Successful conversion? */
  j = strtod ( argv[2], &ptr);               /* Convert to a double float. */
  if ( ptr == argv[2] ) usage();             /* Successful conversion? */

/*
** This statement uses the "ternary conditional assignment" language
** construction to assign the value required to the message indexing variable.
** Note that this concept is efficient in both the generation of machine code
** output ( compile the program with a -S switch and have a look ) and in the
** ease with which it can be understood. The assignment is obvious instead of
** being buried under a litter of `if' and `else' keywords.
*/

  message_index = ( i == j ) ? EQUAL : ( i > j ) ? BIGGER : SMALLER;

/*
** Now print the message.
*/

  (void) printf ( "\n%s%s%s\n\n",     /* Format string specifying 3 strings. */
                  messages[ FIRST ],                   /* Address of string. */
                  messages[ message_index ],           /*        ditto.      */
                  messages[ SECOND ]                   /*        ditto.      */
                  );
  return ( SUCCESS );
  }

  Well as you can no doubt gather it simply compares two numbers on the command line and ejects a little message depending on the relative magnitude of the numbers. In the UNIX tradition the help message is perhaps somewhat
terse, but it serves the purpose of getting you - the student - to think about the importance of creating programs which always cope with nonsensical input in a civilised way. Here are the lines of output.

Usage: if_else_demo <numeric argument 1> <numeric argument 2>
The first argument is equal to the second
The first argument is smaller than the second
The first argument is bigger than the second

  Now that the international community is shrinking with vastly improved telecommunications, it is perhaps a good idea to think carefully about creating programs which can talk in many languages to the users. The method of choice is - I believe - that presented above. The #if defined( LANGUAGE ) gives us an easy method of changing the source code to suit the new sales area. Another possibility is to put all the text output needed from a program into a file. The file would have to have a defined layout and some consistent way of `getting at' the message strings.

  From a commercial point of view this may or may not be a good business plan. Quite definitely it is an absolute no no to scatter a mass of string literals containing the messages and message fragments all over your program script.

  There are two more methods of altering the program flow.

  1 ) The goto a label.
  2 ) The setjump / longjmp library routines.

  The concept of the go to a label construction has had reams of literary verbiage written about it and this author does not intend to add to the pile. Suffice it to say that a goto is a necessary language construct. There are a few situations which require the language to have ( in practice ) some form of unconditional jump. Treat this statement with great caution if you wish your code to be readable by others. An example of legitimate use.
 
  for ( a = 0; a < MATRIX_SIZE; a++ )
  {
    for ( b = 0; b < MATRIX_SIZE; b++ )
    {
      if ( process ( matrix, a, b )) goto bad_matrix;
      }
     }
   return ( OK );

bad_matrix:

   perror ( progname, "The data in the matrix seems to have been corrupted" );
   return ( BAD );

 This is one of the very few "legitimate" uses of goto, as there is no "break_to_outer_loop" in `C'. Note that some compilers complain if the label is not immediately followed by a statement. If your compiler is one of these naughty ones, you can put either a `;' or a pair of braces `{}' after the `:' as a null statement.

  An example of a program package which makes extensive use of the goto is the rz and sz modem communications protocol implementation by Chuck Forsberg of Omen Technology. You should download it and study the code, but do remember that the proof of the pudding argument must apply as the rz & sz system has become extremely popular in its application because it works so well.

  The other method of changing program flow is the setjump and longjmp pair of library functions. The idea is to provide a method of recovery from errors which might be detected anywhere within a large program - perhaps a compiler, interpreter or large data acquisition system. Here is the trivial example:
 

#ident "set_jmp_demo.c"

#include <stdio.h>
#include <setjmp.h>

jmp_buf save;

main()
{
  char c;

  for ( ;; )                     /* This is how you set up a continuous loop.
*/
  {
    switch ( setjmp( save ))
    {
case 0:
      printf ( "We get a zero returned from setjmp on setup.\n\n");
      break;                   /* This is the result from setting up. */

case 1:
      printf ( "NORMAL PROGRAM OPERATION\n\n" );
      break;

case 2:
      printf ( "WARNING\n\n" );
      break;

case 3:
      printf ( "FATAL ERROR PROGRAM TERMINATED\n\nReally Terminate? y/n: " );
      fflush ( stdout );
      scanf ( "%1s", &c );
      c = tolower ( c );
      if ( c == 'y' ) return ( 1 );
      printf ( "\n" );
      break;

default:
      printf ( "Should never return here.\n" );
      break;
      }
    process ();
    }
  }

process ()
{
  int i;

  printf ( "Input a number to simulate an error condition: " );
  fflush ( stdout );
  scanf ( "%d", &i );
  i %= 3;
  i++;                /* So that we call longjmp with  0 < i < 4 */
  longjmp ( save, i);
  }

  Although in this silly little demo the call to longjmp is in the same file as the call to setjmp, this does not have to be the case, and in the practical situation the call to longjmp will be a long way from the call to setjmp. The mechanism is that setjmp saves the entire state of the computer's CPU in a buffer declared in the jmp_buf save; statement and longjmp restores it exactly with the exception of the register which carries the return value from longjmp. This value is the same as the second argument in the longjmp call - i in our little demo. This means, of course, that the stack and frame pointer registers are reset to the old values and all the local variables being used at the time of the longjmp call are going to be lost forever. One consequence of this is that any pointer to memory allocated from the heap will also be lost, and you will be unable to access the data stored in the buffer. This is what the jargonauts call "memory leakage", and is really very difficult bug to find. Your program runs out of dynamic memory long before it should. Take care. So you have to keep a record of the buffers' addresses and free them before the call to longjmp.

More details later on when we learn about the heap memory allocation routines.
----