ANSI C


the ANSI standard was completed in 1989 and ratified as ANSI X3.159-1989 "Programming Language C". this version of the language is often referred to as "ANSI C". later on sometimes the label "C89" is used to distinguish it from C90 but using the same labeling method. the same standard as C89 was ratified by the International Organization for Standardization as ISO/IEC 9899:1990, with only formatting changes, which is sometimes referred to as "C90". therefore, the terms "C89" and "C90" refer to essentially the same language

if you are concerned about strict adherence to the ISO standard, you should use the -ansi option when you compile your programs with the gcc. and if you want be precious then use -Wall option

you are encouraged to conform to the standards, as doing so helps portability between compilers


  • ADT
  • refs
  • strings
  • maths
  • memory
  • file IO
  • exceptions
  • CGI

  • comments

    start with /* and finish with */

    keywords

    
      auto      double     int        struct
    
      break     else       long       switch
    
      case      enum       register   typedef
    
      char      extern     return     union
    
      const     float      short      unsigned
    
      continue  for        signed     void
    
      default   goto       sizeof     volatile
    
      do        if         static     while
     

    identifiers

    an identifier is a sequence of nondigit characters (including the underscore _ and the lower-case and upper-case letters) and digits. the first character shall be a nondigit character

             const int *ptr_to_constant ;
             int *const constant_ptr ;
             
             typedef int *int_ptr ;
             const int_ptr const_ptr ;

    there are four kinds of scopes: file, block, function and function prototype

    a label name is the only kind of identifier that has function scope. it can be used in a goto statement anywhere in the function in which it appears, and is declared implicitly by its syntactic appearance followed by a : and a statement

    types

    int
    main (int argc , char** argv , char** envp)
    {
      int             Iv ;
      long int        Lv ;
      signed int      Pv ;
      unsigned int    Kv ;
      short int       Sv ;
      
      _Bool B1var = 0 ;
      _Bool B2var = 1 ;
    
      float           Fv ;
      double          Dv ;
      long double     Dd ;
      
      return  sizeof (Fv) ;
    }

    приведение типов:

    
        int   atoi (char* s)
        long  atol (char* s)
    
        float atof (char* s)


    blocks

    int
    main (int argc, char** argv, char** envp)
    {
      int x = 11 ;
      
      { int x = 12 ; /* not visible outside this block */  }
      
      return x ;  /* return 11 */
    }

    jumps

       goto label_identifier ;
    
       continue ;
    
       break ;
    
       return expression ;
    


    conditions

    if

            if (condition) { ... }
            else { ... }
          

    switch

    #include <stdio.h>
    #include <stdlib.h>
    
    int
    main (int argc, char** argv, char** envp)
    {
      if (argc != 2) { puts ("usage: prg arg") ; exit (1) ; } ;
      
      int x = atoi (argv[1]) ;
      switch (x)
      {
        case 1 : puts ("1") ; break ;
        case 2 : puts ("2") ; break ;
        default: puts ("3") ; break ;
      } ;
    
      return 0 ;
    }
          
    


    cycles

    for

          for (  expression-1 ;  expression-2 ;  expression-3 )  { statement(s) }
      

    while

        while (cond_1) {
        
          /*...*/
          while (cond_2) {
    
            /*...*/
    
            if (cond_3) goto end_loop1;
    
            /*...*/
    
          }
    
          /*...*/
    
          end_loop1: ;
        }  

    do

        do {
          /* ... */
        while (cond)
      

    advices

    dont use global variables! never-ever!

    use macro #define for that

    #include <stdio.h>
    
    #define MAX_VALUE 100    /* global but not variable ! */
    
    int
    main (int argc, char** argv, char** envp)
    {
      printf ("%i\n", MAX_VALUE) ;
      return 0 ;
    } 

    if you need the state - use static variable in function

    #include <stdio.h>
    
    void
    myfAunc (void) ;
    
    int
    main (int argc, char** argv, char** envp)
    {
      myfAunc () ;
      myfAunc () ;
      myfAunc () ;
      return 0 ;
    }    
    
    void
    myfAunc ()
    {
      static count = 0 ;
      printf ("%i\n", count++) ;
    }

    and you can do it with ADT also. for example:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct 
    {
      int       num ;
      double    factor ;
    } XSTRUCT ;
    
    XSTRUCT myfun (int) ;
    
    float
    main (int argc, char** argv, char** envp)
    {
    
      XSTRUCT r ;
      
      r = myfun (2) ;
      printf ("%2i * e = %7.4f\n", r.num , r.factor) ;
      r = myfun (3) ;
      printf ("%2i * e = %7.4f\n", r.num , r.factor) ;  
      r = myfun (4) ;
      printf ("%2i * e = %7.4f\n", r.num , r.factor) ;
    
      return 0 ; 
    }
    
    XSTRUCT myfun (int x)
    {
      static XSTRUCT mystate ;
    
      mystate.num    = mystate.num + x ;
      mystate.factor = mystate.num * 2.71828 ;
      
      return mystate ;
    } 

    always use heading file as interface to your module(s)

    interface - file ifcace.h :

    #define MAX 100
    #define VAL 12.8
    
    enum { RED = 1 , GREEN = 2 , YELLOW = 7 } ;
    
    int    myfunc1 (int) ;
    double myfunc2 (double) ;
    

    implementation - file module1.c :

    int myfunc1 (int x)
    {
      return x + 2 ;
    } 

    implementation - file module2.c :

    double myfunc2 (double x)
    {
      return x * 2 ;
    }
    

    main program - file main.c :

    #include <stdio.h>
    #include "iface.h"
    
    int
    main (int argc, char** argv, char** envp)
    {
      double x = myfunc2 (VAL + GREEN) ;
      printf ("%7.2f\n", (x + (double) myfunc1 (MAX)) ) ;
      
      return 0 ;
    }

    and now in shell: gcc -Wall module1.c module2.c main.c


    refs

    C89 has no "pass-by-reference" functionality - all params are passed by value and & just means "addressof" and is a way to formulate a pointer from a variable. however, consider this:

    void func (int* x) { *x = 4; }
    
    void callfunc () { int x = 7 ; func (&x) ; }
    

    you're passing the value of the address of the local variable 'x' to 'func' as a pointer argument. a pointer is a memory address containing another memory address. underneath a reference to object is typically the machine address of the object - attach gdb to a process and you'll be able to see this

    what happens if you assign to a reference?

    you change the state of the referent ('referent' is the object to which the reference refers)

    what happens if you return a reference?

    the function call can appear on the left hand side of an assignment operator

    example of usage of a pointer to a function

    #include <stdlib.h>
    
    int one (void) { return 1 ; } ;
    int two (void) { return 2 ; } ;
    
    int
    main (int argc, char** argv, char** env)
    {
      int (*z) () ;
      
      if (atoi (argv[1]) > 10) { z = &one ; } else { z = &two ; } ;
      
      return z() ;  
    }  

    example of passing the pointer to the function to another function

    int myfun (int) ;
    
    int
    twice (int fp(int), int x) { return fp (x) ; }
    
    int
    main (int arc, char** argv, char** env)
    {
      return twice (&myfun, 6) ;
    }
    
    int
    myfun (int x) { return x * 2 ; } 
    


    ADT

    one very simple way: enum type. for exampe :

    enum { RED = 1 , GREEN = 2 , YELLOW = 7 } ;
    
    syntax -
      enum identifier { enumerator-list } 

    and two more complicated ways : stuct and union. for example :

    #include <stdio.h>
    #include <stdlib.h>
    
    /*     product type      */
    typedef struct 
    {
      int       num ;
      double    factor ;
      char*     name ;
      int       rate[10] ;
    } XSTRUCT ;
    
    /*      sum type       */
    typedef union 
    {
      int              num ;
      char             sign ;
    } YUNION ;
    
    int
    main (int argc, char* argv, char* envp)
    {
      YUNION  z ;
      XSTRUCT k ;
    
      scanf  ("%i",    &(z.num)) ;
      printf ("%i\n",  z.num) ;
      
      return 0 ;
    }
    

    ADT can be nested! for example:

    #include "stdio.h"
    
    typedef union
    {
      char* descript ;
      int   identifyer  ;
    } Sum ;
    
    typedef struct 
    {
       char* name ;
       float salary ;
    } Product ;
    
    typedef union
    {
      Sum     data1 ;
      Product data2 ;
    } Complex1 ;
    
    typedef struct
    {
      Product set1 ;
      Sum     set2 ;
    } Complex2 ;
    
    int
    main (int arc, char** argv, char** env)
    {
      Sum y = { .descript = "Bob" } ;
      
      Complex1 x = { .data1 =  y
                   , .data2 =  { .salary = 3.2
                               , .name = "Alice"
                               }
      } ;
    
      printf ("salary %12.3f\n", x.data2.salary) ;;
    
      Sum a = { .identifyer = 14 } ;
      Complex2 b = { .set1 = { .salary = 4.7
                             , .name = "Bob"
                             }
                   , .set2 = a } ;
      
      printf ("name %12s\n", b.set1.name) ;  
    
      return 0 ;
    }

    to assigning a value to an array with in a structure:

           typedef struct _temp_t {
                int16_t _values[4];
           } TEMP_T;
    
           void func (void) {
               TEMP_T *temps;
               int x = 5;
    
               temps._values[0] = x;
           }
    getting an error :
           ... error: request for member '_values' in something not a structure or union  
    
           TEMP_T *temps;

    drop the * from that line. then it will be a true TEMP_T object instead of a pointer. alternatively, malloc some memory to temps and then use:

      temps->_values[0] = x;

    function as a struct attribute

    int myfun (int) ;
    
    typedef struct 
    {
      int             age ;
      int             (*funPtr) (int) ;
    } F ;
    
    int
    main (int arc, char** argv, char** env)
    {
      F a ;
      a.age = 32 ;
      a.funPtr = &myfun ;
      return a.funPtr (a.age) ;
    }
    
    int
    myfun (int x) { return x * 2 ; }
    


    strings <string.h>

    a notorious source of program bugs is trying to put more characters in a string than fit in its allocated size. when writing code that extends strings or moves characters into a pre-allocated array, you should be very careful to keep track of the length of the text and make explicit checks for overflowing the array. many of the library functions do not do this for you! remember also that you need to allocate an extra byte to hold the null character that marks the end of the string

    parsing with strtok

    it's fairly common for programs to have a need to do some simple kinds of lexical analysis and parsing, such as splitting a command string up into tokens. you can do this with the strtok function, declared in the header file string.h

             char * strtok (char *restrict newstring, const char *restrict delimiters)  

    a string can be split into tokens by making a series of calls to the function strtok. the string to be split up is passed as the 'newstring' argument on the first call only. the strtok function uses this to set up some internal state information. subsequent calls to get additional tokens from the same string are indicated by passing a null pointer as the newstring argument. calling strtok with another non-null newstring argument reinitializes the state information

    the 'delimiters' argument is a string that specifies a set of delimiters that may surround the token being extracted. all the initial characters that are members of this set are discarded. the first character that is not a member of this set of delimiters marks the beginning of the next token. the end of the token is found by looking for the next character that is a member of the delimiter set. this character in the original string newstring is overwritten by a null character, and the pointer to the beginning of the token in newstring is returned. on the next call to strtok, the searching begins at the next character beyond the one that marked the end of the previous token. note that the set of delimiters do not have to be the same on every call in a series of calls to strtok

    if the end of the string newstring is reached, or if the remainder of string consists only of delimiter characters, strtok returns a null pointer


    math <math.h>

    arguments and returned values are double

    trigonometry sin(x), cos(x), tan(x)
    inverse trigonometry asin(x), acos(x), atan(x) arctan(y/x) atan2(y,x)
    hyperbolic trigonometry sinh(x), cosh(x), tanh(x)
    exp , logs exp(x), log(x), log10(x)
    remainders modf(x,*ip), fmod(x,y)
    powers pow(x,y), sqrt(x)
    rounding ceil(x), floor(x), fabs(x)

    you should compile the source with -lm option


    memory

    there are two library functions provided by C defined under <stdlib.h> header file to facilitate dynamic memory allocation:

    
        // ptr = (cast-type*) malloc (byte-size)
        int* ptr = malloc (100 * sizeof(int)) ;
        free (ptr) 
    

    malloc() and free() don't work the same way on every OS. malloc() is system/compiler dependent. malloc() gets memory from the kernel when it needs it, and typically in units of pages. memory can only be removed from your program in units of pages, and even that is unlikely to be observed

    since you may only want 4 bytes, and the OS needs to work in pages (often 4k), malloc() takes a page, and puts it's own information in there so it can keep track of what you have allocated and freed from that page. when you call free() , it takes your pointer, backs up to where it's data is, and operates on that


    file IO

          /* read from file (first arg)  */
          /* clang -std=c89 file_write.c */
          
    #include <stdio.h>
    
    int
    main (int argc, char **argv, char** envp) {
    
      char input_str[256] ;
    
      FILE *fd = fopen (argv[1], "r") ;
      fscanf (fd, "%255[^\n]", &input_str) ;
      fclose (fd) ;
     
      printf ("%s\n", input_str) ;
    
      return(0);
    } 

          /* write to file (first arg) with word (secont arg)  */
          /* clang -std=c89 file_write.c */
    
    #include <stdio.h>
    
    int
    main (int argc, char **argv, char** envp) {
    
      FILE *fd = fopen ("something.txt", "w") ;
      fprintf (fd, "hello %s", "Hello & goodby") ;
      fclose (fd) ;
    
      return(0) ;
    } 


    exceptions

    пример - поиск в игрушечной базе данных

    главную функцию программы поместим в файл main.c

    // file main.c
    
    extern void init (void) ;
    extern char* countryName (int) ;
    
    int 
    main (void) 
    {
      init () ;
    
      for ( ; ;) 
      {
        int uid ;
        printf ("userID : ") ;
        scanf ("%i", &uid) ;
        printf ("%s \n", countryName (uid)) ; 
      } ;
    
      return 0 ;
    }
        

    заголовочный файл базы:

    // file simpleDB.h
    
    #include <stdio.h>
    #include <string.h>
    
    struct user  { char* name ;   int   uid ;     } ;
    struct phone { char* number ; char* name ;    } ;
    struct code  { char* code ;   char* country ; } ;
    
    struct user  users [5] ;
    struct phone phones[4] ;
    struct code  codes [2] ;
    
    char mcod [4] ;
    
    char* nullUser    = "user not found" ;
    char* nullPhone   = "phone not found" ;
    char* nullCode    = "code not found" ;
    char* nullCountry = "country not found" ;
        

    код базы :

    // file simpleDB.c
    
    #include "simpleDB.h" 
    
    void 
    init (void) 
    {
      struct user a, b, c, d, s ;
      struct code e, f ;
      struct phone g, h, i, j ;
    
      a.name = "Neo" ;        a.uid = 1 ;             users[0] = a ;
      b.name = "Cipher" ;     b.uid = 2 ;             users[1] = b ;
      c.name = "Morpheus" ;   c.uid = 3 ;             users[2] = c ;
      d.name = "Trinity" ;    d.uid = 4 ;             users[3] = d ;
      s.name = "Switch" ;     s.uid = 5 ;             users[4] = s ;
    
      g.number = "123444" ;   g.name = "Neo" ;        phones[0] = g ;
      h.number = "456555" ;   h.name = "Cipher" ;     phones[1] = h ;
      i.number = "567888" ;   i.name = "Morpheus" ;   phones[2] = i ;
      j.number = "" ;         j.name = "Trinity" ;    phones[3] = j ;
    
      e.country = "USA" ;     e.code = "123" ;        codes[0] = e ;
      f.country = "RUS" ;     f.code = "456" ;        codes[1] = f ;
    
      printf ("db initiated\n") ;
    }
    
    char* 
    findUser (int d) 
    {
      int i = 0 ;
      for (i ; i < 5 ; i++)
        if (users[i].uid == d) return users[i].name ;
      return nullUser ;
    } ;
    
    char* 
    getPhone (char* u) 
    {
      int i = 0 ;
      for (i ; i < 4 ; i++)
        if (phones[i].name == u) return phones[i].number ;
      return nullPhone ;
    } ;
    
    char* 
    getCode (char* p) 
    {
      //  ---- dirty hack: 
      *mcod = '\0' ;
      strncpy(mcod, p, 3) ;
    
      //  ---- canonical: 
      // memcpy(mcod, p, 3) ;
      // mcod[3] = '\0' ; 
    
      if (strlen (mcod) == 3) return mcod ;
      else return nullCode ;
    } ;
    
    char* 
    findByCode (char* mycod) 
    {
      int i = 0 ;
      for (i ; i < 2 ; i++)
        if (!strcmp (mycod, codes[i].code)) return codes[i].country ; 
      return nullCode ;
    }
    

    будем кодить функцию char* countryName (int), помещая сей продукт в файл work.c

    // file work.c
    
    #include <setjmp.h>
    
    extern char* nullUser ;
    extern char* nullPhone ;
    extern char* nullCode ;
    extern char* nullCountry ;
    
    char* 
    countryName (int d) 
    {
      char *ans ;
    
      jmp_buf b ;
      int k = setjmp (b) ;
    
      if (k == 0) 
      {
        ans = findUser     (d) ; if (!strcmp (ans, nullUser))    longjmp (b, 1) ;
        ans = getPhone   (ans) ; if (!strcmp (ans, nullPhone))   longjmp (b, 2) ;
        ans = getCode    (ans) ; if (!strcmp (ans, nullCode))    longjmp (b, 3) ;
        ans = findByCode (ans) ; if (!strcmp (ans, nullCountry)) longjmp (b, 4) ;
        return ans ; 
      }
    
      return 
        k == 1 ? nullUser : 
          k == 2 ? nullPhone :
            k == 3 ? nullCode :
              k == 4 ? nullCountry : "unknown error" ; 
    }
    

    CGI

    cgi - get method

    #include <stdio.h>
    #include <stdlib.h>
    
    int 
    main (int argc, char* argv[])
    {
      printf ("%s\n", getenv ("QUERY_STRING"));
    
      return 0;
    }
        

    cgi - post method

    POST data is appended to the request header, after a double newline. in a CGI-BIN environment, you read it from STDIN:

    #include <stdio.h>
    #include <stdlib.h>
    
    int 
    main (int argc, char* argv[])
    {
      const char * __restrict__    len_ = getenv ("CONTENT_LENGTH") ;
    
      int len = strtol (len_, NULL, 10) ;
    
      char* postdata ;
      postdata = malloc (len + 1) ;
    
      fgets (postdata, len + 1, stdin) ;
      printf ("your data is : %s\n", postdata) ;
    
      free (postdata) ;
    
      return 0 ;
    }
    

    be warned that the server is not required to send you an EOF character (or some termination indicator) at the end of the POST data. never read more than CONTENT_LENGTH bytes