Author Topic: Six Fast Steps to Programming in C  (Read 32894 times)

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Six Fast Steps to Programming in C
« on: February 18, 2011, 04:32:35 AM »
Update:  please see this next link:


The following quick tutorial may be of interest to new students of C ...


Update: Now includes examples of using C strings with recently updated Cvec, Clist and readLine ... to read in dynamic C strings of any length.  This will add 2 more example programs, example 7. and example 8. to the 1. to 6. example C programs that follow. Please see this next link for other example programs using Cvec and Clist:

http://developers-heaven.net/forum/index.php/topic,466.0.html

And this next link also:

http://developers-heaven.net/forum/index.php/topic,2473.msg2740.html#msg2740

Note: when you have exhausted these following pages, you may like to see the latest versions of Cvec and Clist in files Cvec2.h and Clist2.h at the following link:

http://developers-heaven.net/forum/index.php?topic=2598.0


The first part of this tutorial consists of a series of 6 example programs that students may like to have for reference and to use as a working 'shell' from which to start a project.

1. The first program is just a most basic 'shell' that compiles, and when run, askes the user to press 'Enter' to exit/continue ...

2. The next program illustrates how to get valid numeric input in a loop and how to ask for more at the end of a do..while loop that calls the function 'more()'

3. The next program demo's a way to get and validate user input using a FUNCTION to get valid input

4. The next three programs progress from inputing numbers into an ARRAY that has the size fixed at compile time ... to using a Cvec ... and then a Clist container to hold as many numbers as the user wishes (limited only by available memory). They also illustrate how to SORT, FIND, or ERASE elements from the respective containers. 

Note: you will need to have the appropriate pair of the following custom header files:  CvecOfInt.h, Cvec.h ... or ... ClistOfInt.h and Clist.h in the same folder as the programs '5.' and '6.' that use them, to compile the programs '5'. and '6.' that use CvecOfInt and ClistOfInt ... Please note that these 4 header files are provided below.

(Note also that all the numeric input example programs here find the sum and average value of the numbers input.)


1.
Code: [Select]
/* shell.c */ /* 2011-07-31 */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#include <stdio.h>


int main()
{

/*
    You may need to 'un-comment' the 'while( getchar != '\n' );' line BELOW,
    to pause your program, IF (char's and) a '\n' char remains after/if 'input'
   
    // effect of line below is same as 'clear/empty' all char's in stdin //
    // while( getchar() != '\n' ); // 'flush' stdin buffer ... //
   
*/

    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
   
    return 0;
}


2.
Code: [Select]
/* more.c */  /* 2016-10-07 */


/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include <stdio.h>


int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    int reply;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    reply = getchar();
    if( reply != '\n' ) while( getchar() != '\n' ); /* flush stdin ... */
    if( reply == 'n' || reply == 'N' ) return 0;
    /* else ... */
    return 1;
}



int main()
{
    int count = 0, sum = 0, testInt;
   
    for( ; ; ) /* an example C/C++ forever loop ... until 'break' */
    {
        fputs( "Enter next integer to sum: ", stdout ); fflush( stdout );
        if( scanf( "%d", &testInt ) == 1 && getchar() == '\n' )
        {
            ++count;
            sum += testInt;
           
            /* make sure stdin buffer is 'empty' before calling 'more()' */
            if( !more() ) break;
        }
        else
        {
            while( getchar() != '\n' ); /* 'flush' stdin ... */
            puts( "Invalid input! Integers only please ..." );
        }
    }
   
    printf( "\nFor %d numbers entered, sum was %d and average was %.2f\n",
            count, sum, (float)sum/count );
           
    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
   
    return 0;
}


3.
Code: [Select]
/* getValidInt.c  */  /* revised 2018-12-20 */


/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* re. strlen, strcpy, strchr... */
#include <ctype.h> /* re, tolower... */
#include <math.h> /* re. log10 ... */
#include <limits.h> /* re. INT_MIN, INT_MAX*/

#define BIG_BUF_LEN 1024
#define MAX_DIG_IN_INT ((int)log10(INT_MAX) + 1)
#define MAX_NAME_LEN 79


char* getLine( const char* msg )
{
    static char buf[BIG_BUF_LEN];
    char* p;
    printf( msg );
    fgets( buf, BIG_BUF_LEN, stdin );

    /* fix fgets ... */
    p = strchr( buf, '\n' );
    if( p )
        *p = 0; /* strip off '\n' char if present */
    else
        while( getchar() != '\n' ); /* flush stdin to end of line */

    return buf;
}

/* since using 'static' buf inside, memory persists ... so can return address */
char* getLineMaxLen( const char* msg, unsigned maxLen )
{
    char* line;
    for( ; ; )
    {
        line = getLine( msg );
        if( line[0] && strlen( line ) <= maxLen )
            break;
        else if( !line[0] )
            printf( "\nBlank line NOT valid here ... \n" );
        else
            printf( "\nMax len of %u char's was exceeded ... \n", maxLen );
    }
    return line;
}

int getChar( const char* msg )
{
    int reply;
    fputs( msg, stdout ); fflush( stdout );
    reply = getchar();
    if( reply != '\n' )
        while( getchar() != '\n' ) ; /* 'flush' stdin buffer */
    return reply;
}

int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    if( tolower( getChar( "More (y/n) ? " )) == 'n' ) return 0;
    /* else ... */
    return 1;
}

/* Simple ...  NO VALID range checking done here. */
int getInt( const char* prompt )
{
    int testInt;
    for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        fputs( prompt, stdout ); fflush( stdout );
        if( scanf( "%d", &testInt ) && getchar() == '\n' )
            return testInt;
        /* else ...*/
        while( getchar() != '\n' ); /* 'flush' stdin ... as we go */
        puts( "Invalid input! Integers only please ..." );
    }
}



/*  returns a valid int in range INT_MIN..INT_MAX */
int getValidInt( const char* prompt )
{
char loc_buf[16];

for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
{
int good = 1, i, len, sign = 1;
char* tmp = getLine( prompt );
char* tmpCpy = tmp;

if( tmp[0] == '+' ) ++tmp;
else if( tmp[0] == '-') { ++tmp; sign = -1; }

len = i = strlen( tmp );
good = ( len > 0 && len <= MAX_DIG_IN_INT );
while( good && --i >= 0 )
good = isdigit( tmp[i] );

if( good && len == MAX_DIG_IN_INT )
{
if( sign == 1 )
{
sprintf( loc_buf, "%d", INT_MAX );
if( strcmp( tmp, loc_buf ) > 0 ) good = 0;
}
else
{
sprintf( loc_buf, "%d", INT_MIN );
if( strcmp( tmp, &loc_buf[1] ) > 0 ) good = 0;
}
}

if( good )
return atoi( tmpCpy );;
/* else ...*/
printf( "\n'%s' is NOT valid input here!\n", tmpCpy ) ;
}
}


/*  returns a valid int in range min..max */
int getValidIntMinMax( const char* prompt, int min, int max )
{
int tmpInt;
for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
{
    tmpInt = getValidInt( prompt );
if( tmpInt >= min && tmpInt <= max )
break;
/* else ...*/
printf( "\nValid input here is an integer in range %d..%d\n",
min, max );
}
return tmpInt;
}

/* accepts only int's in range 0..max */
int getValidIntPosMax( const char* prompt, int max )
{
    return getValidIntMinMax( prompt, 0, max );
}





int main()
{
    char name[MAX_NAME_LEN+1];
    char gender;
    int count = 0, id, testInt;
    double sum = 0.0;

    printf( "Max number of digits in a positive int is %d\n", MAX_DIG_IN_INT );
    printf( "\nINT_MIN = %d, INT_MAX  = %d\n\n", INT_MIN, INT_MAX );

    do
    {
        strcpy( name, getLineMaxLen( "Enter name: ", MAX_NAME_LEN ));
        id = getValidIntMinMax( "Enter ID in range 101..999: ", 101, 999 );
        for( ; ; )
        {
            gender = toupper(getChar( "Enter gender (m/f): " ));
            if( gender == 'M' || gender == 'F' )
                break;
            /* else ... */
            printf( "\nTo proceed, you must enter either m or f ... try again.\n" );
        }
        testInt = getValidIntMinMax( "Enter next integer score to sum in range 0..100: ", 0, 100 );
        sum += testInt;
        ++ count;

        printf( "Hi %s, your ID is: %d, your sex is: %c\n", name, id, gender );
    }
    while( more() ); /* make sure stdin is 'empty' before calling 'more()' */

    printf( "\nFor %d scores entered, sum was %.0f and average was %.2f\n",
    count, sum, sum/count );



    sum = 0.0;
    count = 0;
    do
    {
        testInt = getInt( "Enter next integer to sum: " );
        sum += testInt;
        ++ count;
    }
    while( more() ); /* make sure stdin is 'empty' before calling 'more()' */

    printf( "\nFor %d values entered, sum was %.0f and average was %.2f\n",
    count, sum, sum/count );


    /* keep 'Window' open until 'Enter' key is pressed ... */
    getChar( "\nPress 'Enter' to continue/exit ... " );
    return 0;
}


4. First an array of integers ...
Code: [Select]
/* arrayInt.c */  /* 2016-10-07 */


/* enters integer data, finds sum, average, sorts, finds value, erase, show ...*/

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include <stdio.h>

#define MAX_SIZE 6 /* using a small size here for simple testing purposes ... */


int getInt( const char prompt[] )
{
    int testInt;
    while( 1 ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        fputs( prompt, stdout ); fflush( stdout );
        if( fscanf( stdin, "%d", &testInt ) == 1 && getchar() == '\n' )
            return testInt;
        /* else ...*/
         while( getchar() != '\n' ); /* 'flush' stdin ... as we go ... */
        puts( "Invalid input! Integers only please ..." );
    }
}

int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    int c, reply;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* 'flush' stdin buffer ...  */
    if( reply == 'n' || reply == 'N' ) return 0;
    /* else ... */
    return 1;
}

long sumAry( const int ary[], int size )
{
    long sum = 0;
    for(  --size; size >= 0; --size ) sum += ary[size];
    return sum;
}

void showAry( const int ary[], int size )
{
    int i;
    for( i = 0; i < size; ++i ) printf( "%d ", ary[i] );
}

int findAry( const int ary[], int size, int value ) /* if found, returns index */
{
    int i;
    for( i = 0; i < size; ++i )
        if( ary[i] == value ) return i;
    /* else if reach here ... */
    return -1;
}

void eraseAry( int ary[], int* size, int index ) /* if valid index erase element */
{
    int i;
    if( index < 0 || index >= *size )
        { printf( "\nERROR! Index %d out of range 0..%d\n", index, *size-1 );
          return; }
    for( i = index; i < *size-1; ++i )
        ary[i] = ary[i+1]; /* copy each element above, down one index, so erased */
    /* now update size and return new size ... (by ref.) */
    -- *size;
}

void isortAry( int ary[], int size )
{
    int i, j, cmp;
    for( i = 1; i < size; ++i ) /* start with an array of just the first 2 elements (if exists) */
    {
        cmp = ary[i]; /* get copy of this new cmp element on each outer loop ... */
        j = i-1; /* get index of element just to the left of the above 'cmp' to start comparisons */
        while( j >= 0 && cmp < ary[j] )
        {
            ary[j+1] = ary[j]; /* copy element 'up' ... */
            --j; /* decrement j in preparation for next inner loop ... */
        }
        ary[j+1] = cmp; /* insert element at index j+1 (since j was decremented above) */
    }
}



int main()
{
    int my_ary[ MAX_SIZE ]; /* create array to hold upto MAX_SIZE int's */
    int i = 0, index, last;
    long sum;       
    do
    {
        my_ary[ i ] = getInt( "Enter next integer to sum: " );
        ++i;
        if( i == MAX_SIZE )
        {
            printf( "You have reached %d, the max size.\n", MAX_SIZE );
            break;
        }
    }
    while( more() ); /* make sure stdin is 'empty' before calling more() */
   
    sum = sumAry( my_ary, i );
   
    printf( "\nFor %d numbers entered, sum was %ld and array average was %.2f\n",
            i, sum, (float)sum/i );
         
    printf( "\nshowAry: " ); showAry( my_ary, i );
   
    last = my_ary[ i-1 ];
   
    printf( "\n\nAfter sorting ..." );
    isortAry( my_ary, i );
    printf( "\nshowAry: " ); showAry( my_ary, i );
         
    index = findAry( my_ary, i, last );
    if( index >= 0 )
    {
        /* Note: below, need to pass address of i ... so i can be updated */
        eraseAry( my_ary, &i, index );

        printf( "\n\nAfter erasing %d ...", last );
        printf( "\nshowAry: " ); showAry( my_ary, i );
    }
    else printf( "%d NOT found in my_ary.\n", last );
                   
    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
   
    return 0;
}


5. Then a Cvec of integers ...
Code: [Select]
/* CvecInt.c */  /* 2016-10-07 */


/* enters integer data, finds sum, average, sorts, finds value, erase, show ...*/

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include "CvecOfInt.h" /* uses/needs custom file: "Cvec.h" */


int getValidInt( const char prompt[] )
{
    for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        int numGood, testInt;
        fputs( prompt, stdout ); fflush( stdout );
        numGood = fscanf( stdin, "%d", &testInt );
        while( getchar() != '\n' ); /* 'flush' stdin ... as we go ... */
        if( numGood == 1 ) return testInt;
        /*else ...*/
        puts( "Invalid input! Integers only please ..." );
    }
}

int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    int c, reply;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* 'flush' stdin buffer ...  */
    if( reply == 'n' || reply == 'N' ) return 0;
    /* else ... */
    return 1;
}
void showCvec( Cvec* cv )
{
    Rec* p = cv->ary;
    for( ; p != &cv->ary[cv->size]; ++p ) printf( "%d ", p->val );
    printf( "\nsizeCvec = %d\n", cv->size );
}



int main()
{
    int index, last;
    long sum;
    Cvec cv; /* create CvecOfInt to hold all the int's to be input */
    Rec tmp; /* typedef struct ... Rec has int member 'val' ... */
    initCvec( &cv );     
    do
    {
        tmp.val = getValidInt( "Enter next integer to sum: " );
        push_backCvec( &cv, &tmp );
    }
    while( more() ); /* make sure stdin is 'empty' before calling more() */
   
    sum = sumCvec( &cv );
   
    printf( "\nFor %d numbers entered, sum was %ld and Cvec average was %.2f\n",
            cv.size, sum, (float)sum/cv.size );
         
    printf( "\nshowCvec: " ); showCvec( &cv );
   
    last = cv.ary[ cv.size-1 ].val;
   
    msortCvec( &cv );
    printf( "\nAfter msortCvec ... " );
    printf( "isSortedCvec( &cv ) = %d\n", isSortedCvec( &cv ) );
    printf( "showCvec: " ); showCvec( &cv );
         
    index = findCvec( &cv, last );
    if( index >= 0 )
    {
        eraseCvec( &cv, index );

        printf( "\nAfter erasing %d ...", last );
        printf( "\nshowCvec: " ); showCvec( &cv );
    }
    else printf( "\n%d NOT found in Cvec.\n", last );
   
    tmp.val = getValidInt( "\nEnter a new integer to isort: " );
    push_backCvec( &cv, &tmp );
    isortCvec( &cv );
    printf( "After isortCvec ... " );
    printf( "isSortedCvec( &cv ) = %d\n", isSortedCvec( &cv ) );
    printf( "showCvec: " ); showCvec( &cv );   
   
   
    clearCvec( &cv );
    printf( "\nAfter clearCvec ...\n" );
    printf( "The size of Cvec is now %d\n", cv.size );   
                   
    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
    return 0;
}
« Last Edit: December 20, 2018, 07:54:19 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #1 on: February 18, 2011, 04:35:54 AM »
6. And finally a Clist of integers ...
Code: [Select]
/* ClistInt.c */  /* 2016-10-06 */

/* enters integer data, finds sum, average, sorts, finds value, erase, show ...*/

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#include "ClistOfInt.h" /* uses/needs custom file: "Clist.h" */


int getValidInt( const char prompt[] )
{
    for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        int numGood, testInt;
        fputs( prompt, stdout ); fflush( stdout );
        numGood = fscanf( stdin, "%d", &testInt );
        while( getchar() != '\n' ); /* 'flush' stdin ... as we go ... */
        if( numGood == 1 ) return testInt;
        /*else ...*/
        puts( "Invalid input! Integers only please ..." );
    }
}

int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    int c, reply;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* 'flush' stdin buffer ...  */
    if( reply == 'n' || reply == 'N' ) return 0;
    /* else ... */
    return 1;
}


void showClist( Clist* cl )
{
    pNode p = cl->start;
    for( ; p != NULL; p = p->next ) printf( "%d ", p->val );
    printf( "size = %d", cl->size );
}



int main()
{
    int last;
    long sum;
    pNode p;
    Clist cl; /* create ClistOfInt to hold all the int's to be input */   
    initClist( &cl );     
    do
    {
        Node tmp; /* typedef struct ... List has int member 'val' ... */
        tmp.val = getValidInt( "Enter next integer to sum: " );
        push_backClist( &cl, &tmp );
    }
    while( more() ); /* make sure stdin is 'empty' before calling more() */
   
    sum = sumClist( &cl );
   
    printf( "\nFor %d numbers entered, sum was %ld and list average was %.2f\n",
            cl.size, sum, (float)sum/cl.size );
         
    printf( "\nshowClist: " ); showClist( &cl );
   
    last = cl.end->val;
   
    msortClist( &cl );
    printf( "\n\nAfter merge sorting ..." );
    printf( "isSorted( &cl ) = %d\n", isSorted( &cl ) );
    printf( "\nshowClist " ); showClist( &cl );
         
    p = findClist( &cl, last );
    if( p != NULL )
    {
        eraseClist( &cl, p );

        printf( "\n\nAfter erasing %d ...", last );
        printf( "\nshowClist: " ); showClist( &cl );
    }
    else printf( "%d NOT found in Clist.\n", last );
   
   
    clearClist( &cl );
    printf( "\n\nAfter clearClist ...\n" );
    showClist( &cl );
                   
    fputs( "\n\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
   
    return 0;
}



And now the header files needed by the above C programs ...

CvecOfInt.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2863.html#msg2863

Note: CvecOfInt.h loads in Cvec.h ... (see link on next page below)
« Last Edit: October 21, 2016, 07:38:08 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #2 on: February 18, 2011, 04:40:05 AM »
Cvec.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2862.html#msg2862

(The 'core' file ... Note: please see the comments at the top of the file, for info re. usage.)
« Last Edit: June 21, 2015, 06:42:12 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #3 on: February 18, 2011, 04:42:51 AM »
ClistOfInt.h
http://developers-heaven.net/forum/index.php/topic,2582.msg2880.html#msg2880

Note: ClistOfInt.h loads in Clist.h ... (see link on next page below)
« Last Edit: July 19, 2011, 02:19:32 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
« Last Edit: July 19, 2011, 02:20:21 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #5 on: February 23, 2011, 04:50:52 AM »
Now here are the two new header files to handle Cvec and Clist when using strings that ALSO use the Cvec.h and Clist.h header files above.

Note 1: these two header files use the new version of file readLine.h  (See readLine.h link below, following the two header files.) This new readLine.h file is the same as the previous readLine.h but now has the added function char* newCopy( const char* str ) This function will make a copy of the C string passed in and return that copy in newly allocated dynamic memory. 

Note 2: the two test programs follow the header files.

First ... CvecOfString.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2865.html#msg2865

Note: CvecOfString.h needs readLine.h and Cvec.h
« Last Edit: August 04, 2011, 05:05:47 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #6 on: February 23, 2011, 04:55:06 AM »
Then ... ClistOfString.h
http://developers-heaven.net/forum/index.php/topic,2582.msg2882.html#msg2882

Note: ClistOfString.h needs readLine.h and Clist.h
« Last Edit: July 19, 2011, 02:26:51 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #7 on: February 23, 2011, 04:59:02 AM »
« Last Edit: July 19, 2011, 02:27:14 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #8 on: February 23, 2011, 05:01:28 AM »
Now the test program for using CvecOfString.h ...

7.
Code: [Select]
/* CvecString.c */  /* testing CvecOfString.h */  /* 2016-06-21 */

/* enter 'word' data, concat, sort, find value, erase, show, unique ... */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#define VEC_CHUNK_SIZE 3
#include "CvecOfString.h" /* that also includes readLine.h that includes
stdio.h, stdlib.h, string.h, ctype.h and the
functions myAssert, newCopy, etc..
                             and then includes Cvec.h */

int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    int c;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    c = getchar();
    if( c != '\n' ) while( getchar() != '\n' ) ; /* 'flush' stdin buffer */
    if( c == 'n' || c == 'N' ) return 0;
    /* else ... */
    return 1;
}


int main()
{
    int index;
    char* last;
    char* concatStr;
    Cvec cv; /* create CvecOfString to hold all the C strings to be input */   
    initCvec( &cv ); /* Note: MUST inital cv for it to work */   
    do
    {
        Rec tmp; /* typedef struct ... Rec has CString member 'str' ... */
        printf( "Enter next 'just one word' to concat: " ); fflush( stdout );
        tmp.str = readLine( stdin );
        push_backCvec( &cv, &tmp );
    }
    while( more() ); /* make sure stdin is 'empty' before calling more() */
   
    concatStr = joinCvec( &cv, "," ); /* returns concated string in new memory */
   
    printf( "\nFor %d words entered, joined words are: %s\n",
            cv.size, concatStr );
    free( concatStr );           
         
    printf( "\nshowCvec: " ); showCvec( &cv );
    printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
   
    last = newCopy( cv.ary[ cv.size-1 ].str ); /* new copy in new memory */
   
    /* isortCvec( &cv ); */
    msort( &cv );
    printf( "\n\nAfter sorting ..." );   
    printf( "\nshowCvec; " ); showCvec( &cv );
    printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
    printf( "\nisSorted( &cv ) = %d\n", isSorted( &cv ) );
         
    index = findCvec( &cv, last );
    if( index >= 0 )
    {
        eraseCvec( &cv, index );

        printf( "\nAfter eraseCvec of the element %s ...", last );
        printf( "\nshowCvec: " ); showCvec( &cv );
        printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
    }
    else printf( "%s NOT found in Cvec.\n", last );
    free( last );
   
    uniqueCvec( &cv );
    printf( "\n\nAfter uniqueCvec ..." );   
    printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
     
/* now clean up all remaining still allocated dynamic memory */
/*//for( index = 0; index < cv.size; ++index ) free ( cv.ary[index].str );
//free( cv.ary ); */
clearCvec( &cv );
printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );

    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
   
    return 0;
}

Or see 7.a

Code: [Select]
/* Cvec_func's.c */  /* testing Cvec_func's.h */  /* 2016-10-08 */


/* enter 'word' data, concat, sort, find value, erase, show, unique ... */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include "readLine.h"  /* readLine.h includes:
                          stdio.h, stdlib.h, string.h, ctype.h
                          and the functions: takeInChar, more,
                          myAssert, newnMem, newCopy, newsubstr etc.. */

typedef struct
{
    char* str;
} Rec ;

void clearRec( Rec* rc )
{
    free( rc->str );
}

#define VEC_CHUNK_SIZE 3
#include "Cvec.h"

void showCvec( const Cvec* cv )
{
    int i;
    for( i = 0; i < cv->size; ++i ) printf( "%s ", cv->ary[i].str );
}

int myRecCmp( const Rec* a, const Rec* b )
{
    return strcmp( a->str, b->str );
}

#include "Cvec_func's.h"


char* joinCvec( Cvec* cv, const char* s )
{
    char* concatStr;
    int i, sumsize = 0;
    int add = strlen( s );
    for( i = 0; i < cv->size; ++i ) sumsize += strlen(cv->ary[i].str) + add;
    concatStr = (char*) malloc( sumsize-add+1 );
    myAssert( (concatStr != NULL), "Error, malloc failed to allocate memory for concatStr" );
    concatStr[0] = 0;
    for( i = 0; i < cv->size; ++i )
    {
        strcat( concatStr, cv->ary[i].str );
        if( i < cv->size-1 ) strcat( concatStr, s );
    }
    return concatStr;
}




int main()
{
    int index;
    char* last;
    char* concatStr;
    Rec tmpRec;
    Cvec cv; /* create CvecOfString to hold all the C strings to be input */   
    initCvec( &cv ); /* Note: MUST inital cv for it to work */   
    do
    {
        Rec tmp; /* typedef struct ... Rec has CString member 'str' ... */
        if( cv.size )
            { printf( " Enter next 'word' to concat: " ); fflush( stdout ); }
        else
            { printf( "Enter first 'word' to concat: " ); fflush( stdout ); }
        tmp.str = readLine( stdin );
        push_backCvec( &cv, &tmp );
    }
    while( more() ); /* make sure stdin is 'empty' before calling more() */
   
    concatStr = joinCvec( &cv, "," ); /* returns concated string in new memory */
   
    printf( "\nFor %d words entered, joined words are: %s\n",
            cv.size, concatStr );
    free( concatStr );           
         
    printf( "\nshowCvec: " ); showCvec( &cv );
    printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
   
    last = newCopy( cv.ary[ cv.size-1 ].str ); /* new copy in new memory */
    tmpRec.str = last;
   
    /* isortCvec( &cv ); */
    msortCvec( &cv, myRecCmp );
    printf( "\n\nAfter sorting ..." );   
    printf( "\nshowCvec; " ); showCvec( &cv );
    printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
    printf( "\nisSortedCvec( &cv, myRecCmp ) = %d\n", isSortedCvec( &cv, myRecCmp ) );
         
    index = findCvec( &cv, &tmpRec, myRecCmp );
    if( index >= 0 )
    {
        eraseCvec( &cv, index );

        printf( "\nAfter eraseCvec of the element %s ...", last );
        printf( "\nshowCvec: " ); showCvec( &cv );
        printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
    }
    else printf( "%s NOT found in Cvec.\n", last );
    free( last );
   
    uniqueCvec( &cv, myRecCmp );
    printf( "\n\nAfter uniqueCvec ..." );   
    printf( "\nshowCvec: " ); showCvec( &cv );
printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );
     
/* now clean up all remaining still allocated dynamic memory */
/*//for( index = 0; index < cv.size; ++index ) free ( cv.ary[index].str );
//free( cv.ary ); */
clearCvec( &cv );
printf( "\nsize = %d, cap = %d\n", cv.size, cv.cap );

    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
   
    return 0;
}


And finally ... the test program for using ClistOfString.h ...

8.
Code: [Select]
/* ClistString.c */  /* test program for new ClistOfString.h */  /* 2016-10-08 */


/* join list of 'words', msort, isort, insert_sorted, find, erase, show, unique */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include "ClistOfString.h" /* that also includes readLine.h that includes:
                              stdio.h, stdlib.h, string.h, ctype.h ...
                              and functions: myAssert, newCopy, takeInChar, more, etc...
                              and then includes Clist.h */
void showMyClist( Clist* cl )
{
    pNode p = cl->start;
    for( ; p != NULL; p = p->next ) printf( "%s ", p->str );
    printf( "\nsizeClist = %d\n", cl->size );
}



int main()
{
    char* last;
    char* concatStr;
    pNode p;
    Clist cl; /* create ClistOfString to hold all the words's to be input */   
    initClist( &cl );     
    do
    {
        Node tmp; /* typedef struct ... List has char* member 'str' ... */
        printf( "Enter next word to join: " ); fflush( stdout );
        tmp.str = readLine( stdin );
        push_backClist( &cl, &tmp );
    }
    while( more() ); /* make sure stdin is 'empty' before calling more() */
   
    concatStr = joinClist( &cl, "," );
   
    printf( "\nFor %d 'words' entered, joined words are: %s\n",
            cl.size, concatStr );

    free( concatStr );             
         
    printf( "\nshowClist: " ); showClist( &cl );
   
    last = newCopy( cl.end->str ); /* new copy in new memory */
   
    isortClist( &cl );
    /* msort( &cl ); */
    printf( "\n\nAfter sorting ..." );   
    printf( "\nshowClist " ); showClist( &cl );
    printf( "\nisSortedClist( &cl ) = %d\n", isSortedClist( &cl ) );
         
    p = findClist( &cl, last );
    if( p != NULL )
    {
        eraseClist( &cl, p );
        printf( "\nAfter erasing Clist of the element %s ...", last );
        printf( "\nshowClist: " ); showClist( &cl );
    }
    else printf( "%s NOT found in Clist.\n", last );
   
    free( last );
   
    uniqueClist( &cl );
    printf( "\n\nAfter unique ..." );
    printf( "\nmy_showClist " );
    showMyClist( &cl );
   
    do
    {
        Node tmp; /* typedef struct ... List has char* member 'str' ... */
        printf( "Enter next word to push_front: " ); fflush( stdout );
        tmp.str = readLine( stdin );
        push_frontClist( &cl, &tmp );
    }
    while( more() );
   
    printf( "\n\nAfter push_front ..." );
    printf( "\nmy_showClist " );
    showMyClist( &cl );


    printf( "Enter next word to insert_sorted: " ); fflush( stdout );
    p = (pNode) malloc( sizeof(Node) );
    p->str = readLine( stdin );
    myAssert( (p != NULL), "Error! malloc failed next word to insert_sorted" );
    insert_sortedClist( &cl, p );

    printf( "\n\nAfter insert_sorted ..." );
    printf( "\nmy_showClist " );
    showMyClist( &cl );

    clearClist( & cl );
                   
    takeInChar( "\nPress 'Enter' to continue/exit ... " );
    return 0;
}

Or see 8.a

Code: [Select]
/* Clist_func's.c */  /* test program for new Clist_func's.h */  /* 2016-10-08 */


/* join list of 'words', msort, isort, insert_sorted, find, erase, show, unique */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include "ClistOfString.h" /* that also includes readLine.h that includes:
                              stdio.h, stdlib.h, string.h, ctype.h ...
                              and the functions: myAssert, newCopy, takeInChar, more, etc...
                              and then includes Clist.h */

void showMyClist( Clist* cl )
{
    pNode p = cl->start;
    for( ; p != NULL; p = p->next ) printf( "%s ", p->str );
    printf( "\nsizeClist = %d\n", cl->size );
}




int main()
{
    char* last = NULL;
    char* concatStr;
    Node tmp; /* typedef struct ... Node has char* member 'str' */
    pNode p;
    Clist cl; /* create ClistOfString to hold all the words's to be input */   
    initClist( &cl );     
    for( ; ; )
    {
        printf( "Enter next word to join: " ); fflush( stdout );
        tmp.str = readLine( stdin );
        if( strlen( tmp.str ) )
        {
            push_backClist( &cl, &tmp );
            if( !more() ) /* make sure stdin is 'empty' before calling more() */
                break;
        }
        else
        {
            free( tmp.str );
            puts( "Empty entries NOT accepted here ... " );
        }
    }

   
    concatStr = joinClist( &cl, "," );
   
    printf( "\nFor %d 'words' entered, joined words are: %s\n",
            cl.size, concatStr );

    free( concatStr );             
         
    printf( "\nshowClist: " ); showClist( &cl );
   
    if( !last  )
        last = newCopy( cl.end->str ); /* new copy in new memory */

    isortClist( &cl );
    /* msort( &cl ); */
    printf( "\n\nAfter sorting ..." );   
    printf( "\nshowClist " ); showClist( &cl );
    printf( "\nisSortedClist( &cl ) = %d\n", isSortedClist( &cl ) );
         
    p = findClist( &cl, last );
    if( p != NULL )
    {
        eraseClist( &cl, p );
        printf( "\nAfter erasing Clist of the element %s ...", last );
        printf( "\nshowClist: " ); showClist( &cl );
    }
    else printf( "%s NOT found in Clist.\n", last );
   
    free( last );
   
    uniqueClist( &cl );
    printf( "\n\nAfter unique ..." );
    printf( "\nshowMyClist " );
    showMyClist( &cl );
   
    do
    {
        Node tmp; /* typedef struct ... List has char* member 'str' ... */
        printf( "Enter next word to push_front: " ); fflush( stdout );
        tmp.str = readLine( stdin );
        push_frontClist( &cl, &tmp );
    }
    while( more() );
   
    printf( "\n\nAfter push_front ..." );
    printf( "\nshowMyClist " );
    showMyClist( &cl );


    printf( "Enter next word to insert_sorted: " ); fflush( stdout );
    p = (pNode) malloc( sizeof(Node) );
    p->str = readLine( stdin );
    myAssert( (p != NULL), "Error! malloc failed next word to insert_sorted" );
    insert_sortedClist( &cl, p );

    printf( "\n\nAfter insert_sorted ..." );
    printf( "\nshowMyClist " );
    showMyClist( &cl );

   
    clearClist( & cl );
                   
    takeInChar( "\nPress 'Enter' to continue/exit ... " );
    return 0;
}
« Last Edit: October 17, 2016, 02:53:06 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #9 on: February 23, 2011, 05:13:34 AM »
Now backing up some ... here is a C version, to parallel the C++ version, to an often seen beginning student problem ... programming a bank account type program to handle balance, deposits and withdrawals. The following program only handles integer input and display.  It illustrates passing and returning values by reference ... and also returning a function value.

Code: [Select]
// bankAccount.c // 2011-02-16 //

// http://developers-heaven.net/forum/index.php/topic,46.0.html

#include <stdio.h>

int getValidInt( const char prompt[] )
{
    for( ;; ) // an example of a C/C++ forever loop ... until 'return'
    {
        int testInt, numGoodValues;
        printf( "%s", prompt );  fflush( stdout );
        numGoodValues = scanf( "%d", &testInt );
        while( getchar() != '\n' ) ; /* flush stdin buffer ... */
        if( numGoodValues == 1 ) return testInt;
        // else ...
        puts( "Invalid input! Integers only please ..." );
    }
}

int more()// defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered
{
    int c, reply;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* flush stdin buffer ... */
    if( reply == 'n' || reply == 'N' ) return 0;
    // else ...
    return 1;
}

int transactions( int* deposits, int* withdrawals )
{
    int net = 0;
    puts( "(Enter a negative value to indicate withdrawal)" );
    for( ;; )
    {
        int amount = getValidInt( "Enter dollars for this transaction (0 to exit)    : " );
        if( amount > 0 ) ++ *deposits;
        else if( amount < 0 ) ++ *withdrawals;
        else return net;
        net += amount;
    }
}

void showResults( int balance, int deposits, int withdrawals, int net_change )
{
    printf( "Opening balance        : %d\n", balance );
    printf( "Number of deposits     : %d\n", deposits );
    printf( "Number of withdrawals  : %d\n", withdrawals );
    printf( "Closing balance        : %d\n", balance + net_change );
}



int main()
{
    do
    {
        int balance = getValidInt( "Please enter your new starting balance (dollars)  : " );
        int deposits = 0, withdrawals = 0;       
        int net_change = transactions( &deposits, &withdrawals ); /* passing addresses ... */
        showResults( balance, deposits, withdrawals, net_change );
    }
    while( more() ); // make sure cin stream is 'empty' before calling 'more()' ...
    return 0;
}


Another often seen beginning student problem ...

Code: [Select]
/* incomeTax.c */ /* 2011-08-29 */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#include <stdio.h>

#define STD_DEDUCT  11000   // in dollars
#define NO_TAX_ON   11000   // in dollars
#define STEP_UP_2   22000   // tax step size in dollars
#define TAX_RATE    0.11    // beginning tax rate of taxable income
#define MAX_FRAC    0.33    // of taxable income


int getValidInt( const char prompt[] )
{
    for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        int numGood, testInt;
        fputs( prompt, stdout ); fflush( stdout );
        numGood = fscanf( stdin, "%d", &testInt );
        while( getchar() != '\n' ); /* 'flush' stdin ... as we go ... */
        if( numGood == 1 ) return testInt;
        /*else ...*/
        puts( "Invalid input! Integers only please ..." );
    }
}

int more() /* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
{
    int c, reply;
    fputs( "More (y/n) ? ", stdout ); fflush( stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* 'flush' stdin buffer ...  */
    if( reply == 'n' || reply == 'N' ) return 0;
    /* else ... */
    return 1;
}

int getTotDeductions()
{
    return getValidInt( "Enter the total sum of all allowable deductions: " );
}


double incomeTax()
{
    int grossIncome = getValidInt( "Enter gross income in dollars: " );
    int deduction = getTotDeductions();
    int netIncome, taxIncome;
    double rate, tax = 0.0, taxIncomeTmp;

    if( deduction < STD_DEDUCT )  deduction = STD_DEDUCT;

   
    if( deduction >= grossIncome ) netIncome = 0;
    else netIncome = grossIncome - deduction;

    taxIncome = netIncome - NO_TAX_ON;
    if( taxIncome < 0 ) taxIncome = 0;
   
    rate = TAX_RATE;
    taxIncomeTmp = taxIncome;

    while( taxIncomeTmp >= STEP_UP_2  &&  tax < taxIncome*MAX_FRAC
                                      &&  rate < MAX_FRAC )
    {
        tax += rate*STEP_UP_2;
        /* printf( "%14.2f\n", rate*STEP_UP_2 ); */
        rate *= (1+TAX_RATE);
        taxIncomeTmp -= STEP_UP_2;
    }
    if( taxIncomeTmp > 0 ) tax += rate*taxIncomeTmp;
    if( tax > taxIncome*MAX_FRAC ) tax = taxIncome*MAX_FRAC;


    printf( "Your deduction: %d\n", deduction );
    printf( "Your netincome: %d\n", netIncome );
    printf( "Your taxincome: %d\n", taxIncome );
    printf( "Your tax  rate: %0.6f\n", ( taxIncome > 0 ? tax/taxIncome : 0 ) );
   
    return  tax;
}



int main()
{
    do
    {
        double tax = incomeTax();
        printf( "Your income tax this year is: %.2f\n", tax );
    }
    while( more() ); // make sure stdout is 'empty' before calling more()

    return 0;
}


Another common student request, a family name and birthday data management example.
Note: you will need to include the file readLine.h available here:
http://developers-heaven.net/forum/index.php/topic,2580.msg2864.html#msg2864

Code: [Select]
/* familyData3File.c */  /* 2016-10-16 */


/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#include "readLine.h" /* Now includes functions: takeInChar, more ... */

#define THIS_YEAR 2016

#define MAX 99 /* max number of family records allowed in array of ... */

#define FNAME "familyData3File.txt"

#define MENU "\na A dd data\n" \
             "s S how all data\n" \
             "o O rder/s O rt all data\n" \
             "u U pdate file\n" \
             "q Q uit\n" \
             "Your choice A, S, U, O, Q: "


typedef struct
{
    char* fname;
    char* lname;
    char* dob; /* enter in YYYYMMDD format like this example 19080524 */
} FamilyData;


int showMenuGetChoice()
{
    return takeInChar( MENU );
}

void show( const FamilyData data[], int size )
{
    int i;
    printf( "The %d family data are:\n", size );
    for( i = 0; i < size; ++ i )
        printf( "%s %s %s\n", data[i].fname, data[i].lname, data[i].dob );
}

int readFile( FamilyData data[], int maxSize )
{
    int size = 0;
    FILE* fin = fopen( FNAME, "r" );
    if( fin )
    {
        while( (data[size].fname = readLine( fin )) != NULL
               && (data[size].lname = readLine( fin )) != NULL
               && (data[size].dob = readLine( fin )) != NULL )
        {

            ++ size;
            if( size == maxSize)
            {
                printf( "The MAX number %d of family data handled "
                        "here has been reached.\n", MAX );
                break;
            }
        }
        fclose( fin );
    }
    else
    {
        printf( "There was an error opening file %s\n", FNAME );
        printf( "Perhaps it has NOT been created yet ...\n"
                "but will be created at the end of this program?\n\n" );
    }
       
    return size;
}

int updateFile( const FamilyData data[], int size )
{
    int i;
    FILE* f = fopen( FNAME, "w" );
    myAssert( (f != NULL), "Error! File NOT opened to write!" );
    for( i = 0; i < size; ++ i )
        fprintf( f, "%s\n%s\n%s\n", data[i].fname, data[i].lname, data[i].dob );
    fclose( f );
    return i;
}

int isLeapYear( int y )
{
    if( y % 4 != 0 ) return 0;
    if( y % 100 == 0 && y % 400 != 0 ) return 0;
    return 1;
}

int daysInMonth( int m, int y )
{   /* ------------===----------- > jan feb mar apr may jun
       ---------------------- >  jul aug sep oct nov dec */
    static int daysInMonth[] = { 0, 31, 28, 31, 30, 31, 30,
                                 31, 31, 30, 31, 30, 31 };
    int days = daysInMonth[m];
    if( m == 2 && isLeapYear( y )) ++days;
    return days;
}

int isValidDate( const char* dob )
{
    char buffer[5] = { 0 };
    int y, m, d;

    if( strlen( dob ) != 8 ) return 0;

    strncpy( buffer, dob, 4 );
    y = atoi( buffer );
    if( y < 1600 || y > THIS_YEAR ) return 0; /* since not valid year here */
 
    strncpy( buffer, dob+4, 2 );
    buffer[2] = 0; /* 0 terminate ... */
    m = atoi( buffer );
    if( m < 1 || m > 12 ) return 0; /* since not valid month ... */

    d = atoi( dob+6 );
    if( d < 1 || d > daysInMonth( m, y )) return 0; /* since not valid day */
   
    /* else ... if reach here ... must be valid date, so ... */
    return 1;   
}

int addMembers( FamilyData data[], int iStart )
{
    if( iStart == MAX )
    {
        printf( "The MAX number %d of family data handled "
                "here has been reached.\n", MAX );
        return iStart;
    }   
   
    do
    {
        char * firstName, * lastName, * dob;
        fputs( "Enter first name: ", stdout ); fflush( stdout );
        firstName = readLine( stdin );
        firstName[0] = toupper( firstName[0] );

        fputs( "Enter last name: ", stdout ); fflush( stdout );
        lastName = readLine( stdin );
        lastName[0] = toupper( lastName[0] );       

        fputs( "Enter dob: ", stdout ); fflush( stdout );
        dob = readLine( stdin );

        printf( "Your entered %s %s %s ... ",
                firstName, lastName, dob );
        if( tolower(takeInChar( "Ok (y/n) ? ")) != 'y' ||
            !isValidDate( dob ))
        {
            if( !isValidDate( dob ) )
                printf( "DOB: %s is NOT valid. ", dob );
            free( dob ); free( lastName ); free( firstName );
            fputs( "Aborted ... try entry again ... ", stdout );
            continue;
        }
       
        data[iStart].lname = lastName; /* copy pointers (addresses) ... */
        data[iStart].fname = firstName;
        data[iStart].dob = dob;
       
        ++ iStart;
        if( iStart == MAX)
        {
            printf( "The MAX number %d of family data handled "
                    "here has been reached.\n", MAX );
            break;
        }
                   
    /* make sure stdin is 'empty' before calling 'choose( "More" )' */
    } while( more() );
    return iStart;   
}

void clean( FamilyData data[], int num )
{
    int i;
    for( i = num-1; i >= 0; -- i )
    {
        free( data[i].dob );
        free( data[i].lname );
        free( data[i].fname );
    }
}

int compareLnameDOB( const FamilyData* a, const FamilyData* b )
{
    int tmp = strcmp(a->lname, b->lname);
    if(  tmp != 0 ) return tmp;
    return strcmp(a->dob, b->dob);
}

/* sorted by last name ...  then DOB */
void isortLnameDOB( FamilyData ary[], int size )
{
    int i, j;
    /* start with an array of just the first 2 elements (if exists) */
    for( i = 1; i < size; ++i )
    {
        /* get copy of this pointer on each outer loop */
        FamilyData cmp = ary[i];
        /* get index of element just to the left of the above 'cmp'
           to start comparisons */
        j = i-1;
        while( j >= 0 && compareLnameDOB(&cmp, &ary[j]) < 0 )
        {
            ary[j+1] = ary[j]; /* copy pointer 'up' ... */
            --j; /* decrement j in preparation for next inner loop ... */
        }
        ary[j+1] = cmp; /* insert pointer at index j+1
                        (since j was decremented above) */
    }
}


int compareFnameDOB( const FamilyData* a, const FamilyData* b )
{
    int tmp = strcmp(a->fname, b->fname);
    if(  tmp != 0 ) return tmp;
    return strcmp(a->dob, b->dob);
}

/* sorted by first names ... then DOB ... */
void isortFnameDOB( FamilyData ary[], int size )
{
    int i, j;
    /* start with an array of just the first 2 elements (if exists) */
    for( i = 1; i < size; ++i )
    {
        /* get copy of this pointer on each outer loop */
        FamilyData cmp = ary[i];
        /* get index of element just to the left of the above 'cmp'
           to start comparisons */
        j = i-1;
        while( j >= 0 && compareFnameDOB(&cmp, &ary[j]) < 0 )
        {
            ary[j+1] = ary[j]; /* copy pointer 'up' ... */
            /* decrement j in preparation for next inner loop ... */
            --j;
        }
        /* insert pointer at index j+1 (since j was decremented above) */
        ary[j+1] = cmp;
    }
}

int compareDOBfname( const FamilyData* a, const FamilyData* b )
{
    int tmp = strcmp(a->dob, b->dob);
    if(  tmp != 0 ) return tmp;
    return strcmp(a->fname, b->fname);
}

/* sorted by DOB ... then fname ... */
void isortDOBfname( FamilyData ary[], int size )
{
    int i, j;
    for( i = 1; i < size; ++i ) /* start with an array of just
                                   the first 2 elements (if exists) */
    {
        FamilyData cmp = ary[i]; /* get copy of this pointer on each outer loop */
        j = i-1; /* get index of element just to the left of
                 the above 'cmp' to start comparisons */
        while( j >= 0 && compareDOBfname(&cmp, &ary[j]) < 0 )
        {
            ary[j+1] = ary[j]; /* copy pointer 'up' ... */
            --j; /* decrement j in preparation for next inner loop ... */
        }
        ary[j+1] = cmp; /* insert pointer at index j+1
                        (since j was decremented above) */
    }
}

void sort( FamilyData fm[], int size )
{
    switch( takeInChar( "Sort by 1. DOB & firstname, "
                        "2. firstname & DOB, 3. lastname & DOB : ") )
    {
        case '1': isortDOBfname( fm, size ); break;
        case '2': isortFnameDOB( fm, size ); break;
        case '3': isortLnameDOB( fm, size ); break;
        default:  puts( "Sort request NOT valid here!" );
    }
}



int main()
{
    FamilyData data[MAX];
    int choice;
    int loop = 1;
   
    int count = readFile( data, MAX );
    if( count ) show( data, count );
   
    while( loop )
    {
        switch( (choice = showMenuGetChoice()) )
        {
            case 'a': case 'A': count = addMembers( data, count ); break;
            case 's': case 'S': putchar( '\n' ); show( data, count ); break;
            case 'o': case 'O': sort( data, count ); break;
            case 'u': case 'U': updateFile( data, count ); break;
            case 'q': case 'Q': loop = 0; break;
            default: printf( "'%c' is NOT a valid choice here!", choice );
        }
    }   

    clean( data, count );
           
    takeInChar( "\nPress 'Enter' to continue/exit ... " );
    return 0;
}

You might also like to see ...

Six Fast Steps to Programming in C++
http://developers-heaven.net/forum/index.php/topic,2019.0.html

Free coding help for beginners in C or C++
http://developers-heaven.net/forum/index.php/topic,2575.0.html

CvecOfInt.h, CvecOfString.h, Cvec.h, Cvec_func's.h (now with FUNCTION POINTERS)
http://developers-heaven.net/forum/index.php/topic,2580.0.html

ClistOfInt.h, ClistOfString.h, Clist.h, Clist_func's.h using FUNCTION POINTERS
http://developers-heaven.net/forum/index.php/topic,2582.0.html

And ...

Cvec of struct
employeeRec's_msortCvec_func's.h.c
http://developers-heaven.net/forum/index.php/topic,2581.0.html

And now a C++ STL vector version of the Cvec of struct program ...
employeeRec's_vector.cpp
http://developers-heaven.net/forum/index.php/topic,2581.msg2875.html#msg2875

Clist of struct
employeeRec's_msortClist_func's.h.c
http://developers-heaven.net/forum/index.php/topic,2583.0.html

And now a C++ STL list version of the Clist of struct program ...
employeeRec's_list.cpp
http://developers-heaven.net/forum/index.php/topic,2583.msg2890.html#msg2890
« Last Edit: October 17, 2016, 02:59:22 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #10 on: August 01, 2011, 03:10:45 AM »
Appendum:  Here is an example of using a Cvec of Clist's ...

Note: uses files Clist.h and Cvec.h provided/linked above ... and here also.

Clist.h
http://developers-heaven.net/forum/index.php/topic,2582.msg2877.html#msg2877

Cvec.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2862.html#msg2862

Note the sequence order, first Clist.h, and then Cvec.h, of the included files, in the following demo program ...

Code: [Select]
/* Cvec_of_struct_with_Clist.c */  /* a demo/test program */  /* 2016-10-08 */


/*
   A simulation of a 'road' ... using a Cvec to hold each road section
   
   -> each Rec (record/struct) in the Cvec holds the ...
      -> new speed limit and also ...
      -> a Clist of points ... for that speed and section of the road.
     
   -> the last Rec holds the final speed and the final point(s) in the 'road'
   
   -> In a loop until done, the program asks/gets from the user ...
      -> each new speed  ... and then, for this new section with that speed,
      -> a Clist of points
     
   -> After 'road' data is all entered, the program calculates and displays
      -> the distance travelled
      -> the time taken
      -> the average speed travelled for the distance and time

   -> Note: this program assumes that 'the speed limit' equals
            ... the average speed travelled during each new section
            ... and sub section
*/

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* re. memcpy */

typedef struct ClistOfPoint
{
    int x;
    int y;
    struct ClistOfPoint* next;
} Node ; /* i.e. ... a node/element/record in the Clist ... */

typedef Node* pNode;

void clearNode( pNode p )
{
    /* no dynamic C strings, etc... to free here ... */
    p = p;
}

void showNode( pNode p ) /* show this point ... */
{
    printf( "(%d, %d) ", p->x, p->y );
}

#ifndef dwMYASSERT
#define dwMYASSERT
void myAssert( int condition, const char text[] )
{
    if( !condition )
    {
        fprintf(stderr, "%s\n", text );
        fputs( "Press 'Enter' to exit program ... ", stderr );
        getchar();
        exit(1);
    }
}
#endif

/* Ok ... NOW can include ... */
#include "Clist.h"

/* and now can define ... */
void showClist( Clist* list ) /* show all points in this Clist ... */
{
    if( list->size )
    {
pNode p = list->start;
for( ; p != NULL; p = p->next )
{ showNode( p ); putchar( '\n' ); } /* each point on a new line */
printf( "Node size = %d\n", list->size );
    }
    else puts( "The list is empty ... " );
}


/* and now ... getting ready for the Cvec of Rec's */
typedef struct
{
    int speed;
    Clist points;
} Rec ;
void clearRec( Rec* rc ) /* clear this Rec ... */
{
    clearClist( & rc->points ); /* clear this list of points */
}

/* Ok ... NOW can include ... */
#include "Cvec.h"



/* fuctions used by main ... */
int getValidInt( const char prompt[] );
int more( const char prompt[] );
float getDist( const Node span[] );
int cal_and_show_stats ( Cvec* cv );




int main() /* /////////////////////////////////////////////////////////////// */
{
    Cvec cv;
    Rec cr;
    int i, spans;
    initCvec( & cv );
    do /* for each 'section' ... */
    {
        initClist( &cr.points ); /* for each new Clist of points ... */
        cr.speed = getValidInt( "Enter next speed: " );
        putchar( '\n' );         
        do /* now get all points at this speed in this section ... */
        {
            Node lr; /* 'lr' used to hold x and y of each new point ... */
            lr.x = getValidInt( "Enter next x: " );
            lr.y= getValidInt( "Enter next y: " );
            push_backClist( &cr.points, &lr ); /* Ok ... add this point ... */
        }   
        while( more( "points" ) );
       
        push_backCvec( &cv, &cr ); /* ok ... add this 'section' ... */
       
        putchar( '\n' ); /* get ready/space for next section*/
    }
    while( more( "sections" ) ); /* make sure stdin 'empty' before call more() */
   
   
    puts( "\n\nShowing vector ..." );
    for( i = 0; i < cv.size; ++i )
    {
        printf( "Points for section %d, with speed %d\n", i+1, cv.ary[i].speed );
        showClist( & cv.ary[i].points ); /* now ... show this set of points ... */
        putchar( '\n' );
    }
   
    spans = cal_and_show_stats( &cv );
    printf( "Number of sections = %d, number of spans = %d\n", cv.size, spans );
   
    clearCvec( &cv ); /* now free all dynamic memory ... */
                     
    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
    return 0;
   
} /* //////////////////////////////////////////////////////////////////////// */



int getValidInt( const char prompt[] )
{
    for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        int numGood, testInt;
        fputs( prompt, stdout ); fflush( stdout );
        numGood = fscanf( stdin, "%d", &testInt );
        while( getchar() != '\n' ); /* 'flush' stdin ... as we go ... */
        if( numGood == 1 ) return testInt;
        /*else ...*/
        puts( "Invalid input! Integers only please ..." );
    }
}

/* defaults to 'true'/'yes'/'1' ... unless 'n' or 'N' entered */
int more( const char prompt[] )
{
    int c, reply;
    printf( "More %s (y/n) ? ", prompt ); fflush( stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* 'flush' stdin buffer ...  */
    if( reply == 'n' || reply == 'N' ) return 0;
    /* else ... */
    return 1;
}

float getDist( const Node span[] ) /* return sqrt( deltaX*deltaX + deltaY*deltaY ); */
{
    return sqrt( (span[0].x-span[1].x)*(span[0].x-span[1].x)
               + (span[0].y-span[1].y)*(span[0].y-span[1].y)  );
}

int cal_and_show_stats( Cvec* cv ) /* ... and return count of spans */
{
    float dd, d = 0, t = 0;
    int spans = 0;
    if( cv->size > 1 || (cv->size && cv->ary[0].points.size > 1) )
    {
        int i;
        Node span[2]; /* Note: a 'span' is an array of 2 points ... */
        Node* p = cv->ary[0].points.start; /* get address of first point ... */
       
        span[0].x = p->x; /* get x of 1st point in span ... */
        span[0].y = p->y; /* get y of 1st point in span ... */

        for( i = 0 ; i < cv->size ;  )
        {
            if( p->next != NULL )
            {
                p = p->next;
                span[1].x = p->x; /* get x of 2nd point in span ... */
                span[1].y = p->y; /* get y of 2nd point in span ... */
                dd  = getDist( span ); /* get distance of this span ... */
                d += dd; /* add to total distance ... */
                t += dd/cv->ary[i].speed; /* find/add time taken ... to total */
                ++ spans;
            }
            else
            {
                ++i;
                if( i == cv->size )  break;

                p = cv->ary[i].points.start;
                span[1].x = p->x;
                span[1].y = p->y;
                dd  = getDist( span );
                d += dd;
                t += dd/cv->ary[i].speed;
                ++ spans;
            }

            printf( "So far, distance = %f, time  = %f "
                    "and average speed = %f\n", d, t, d/t );

            span[0].x = span[1].x; /* update ... 1st point in next span ...*/
            span[0].y = span[1].y;
        }
    }
   
    printf( "\n\nFinals:\n"
            "Total distance = %f and time = %f and average speed = %f\n",
            d, t, d/t );
           
    return spans;
}

Here is a link to a C++ version of the above 'road  simulation' ...
http://developers-heaven.net/forum/index.php/topic,2019.msg2901.html#msg2901
« Last Edit: October 17, 2016, 03:01:22 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #11 on: August 03, 2011, 02:41:27 AM »
A C emulation of one C++ solution to a very interesting student problem to find a record in a Clist of records ...  using ... 'none' to 'all' ... of multiple search pararmeters ...


Note: uses file Clist.h ... (to emulate the C++ STL list container) ... see the link to file provided/linked above ... and here also.

Clist.h
http://developers-heaven.net/forum/index.php/topic,2582.msg2877.html#msg2877

Code: [Select]
/* Clist_of_struct.c */  /* 2016-10-08 */


/* an interesting student problem to find a record in a list of records ... */
/* using ... 'none' to 'all' ... of multiple search pararmeters */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */


#include "readLine.h" /* includes stdio.h, stdlib.h, string.h, takeInChar, more, etc... */

#define FILE_NAME "student.dat"

const char* CODES =
    "ALL NONE n=lastName i=Id d=Dob(yymmdd) g=Gender(m,f) s=Status(F,S,J,S,G)";


typedef struct myStudent
{
    char*   lname;  /* last name */
    int     id,
            dob;    /* yyyymmdd */
    char    gender, /* 'M' or 'F' */
            status; /* 'F' or 'S' or 'J' or 'G' */

    struct  myStudent* next;
} Node ;

typedef Node* pNode;

void clearNode( Node* p )
{
    free( p->lname );
}

#define Student Node /* from here on ... equate 'Node' with 'Student' */

/* Ok ... now can ... */
#include "Clist.h"


int getValidInt( const char* prompt );

void showStudent( const Student* s );
void showClist( const Clist* s );
void inputStudent( Student* s );

int hasChNotInStr( const char* t, const char* s );
int matchList( const Student* m, const Student* t, const char* matchStr );
void showMatching( const Clist* s );

void saveToFile( const Clist* myList );
void fillFromFile( FILE* fin, Clist* myList );




int main() /* ////////// BEGIN MAIN ///////////////////// */
{
    FILE* fin;
    Clist  myLst; /* create an empty list to hold Students ... */
    initClist( &myLst );
    fin = fopen( FILE_NAME, "r" );

    if( fin )
        fillFromFile( fin, &myLst );
    else /* get some data to work with ... */
    {
        do
        {
            Student tmp;
            inputStudent( &tmp );
            push_backClist( &myLst, &tmp );
           
        }   /* make sure stdin is 'empty' before calling 'more' */
        while( more() );
       
        saveToFile( &myLst ); /* create/store file ... */
    }
   
    if( myLst.size)
    {
        showClist( & myLst );
        putchar( '\n' );
    }

    do
    {
        showMatching( &myLst );
    }
    while( more() );
   
    clearClist( &myLst );

    takeInChar( "\nPress 'Enter' to continue/exit ... " );
    return 0;
   
} /* ///////////////// END MAIN ///////////////////////// */




int getValidInt( const char* prompt )
{
    for( ; ; ) /* an example of a C/C++ forever loop ... until 'return' */
    {
        int numGood, testInt;
        fputs( prompt, stdout ); fflush( stdout );
        numGood = fscanf( stdin, "%d", &testInt );
        while( getchar() != '\n' ); /* 'flush' stdin ... as we go ... */
        if( numGood == 1 ) return testInt;
        /*else ...*/
        puts( "Invalid input! Integers only please ..." );
    }
}

void showStudent( const Student* s )
{
    printf( "%s %d %d %c %c", s->lname, s->id, s->dob, s->gender, s->status );
}
void showClist( const Clist* s )
{
    pNode i = s->start;
    for(  ; i != NULL; i = i->next )
        { showStudent( i ); putchar( '\n' ); }
}

void inputStudent( Student* s )
{
    for( ;; )
    {
        s->id = getValidInt( "Enter id number        : " );
        s->dob = getValidInt( "Enter dob (yyyymmdd)   : " );

        s->gender = toupper(takeInChar( "Enter gender (M/F)     : " ));
        s->status = toupper(takeInChar( "Enter status (F/S/J/S/G) : " ));
     
        fputs( "Enter last name        : ", stdout ); fflush( stdout );
        s->lname = strToUpper(readLine( stdin ));
       
        printf( "You entered: %s %d %d %c %c\n",
                 s->lname, s->id, s->dob, s->gender, s->status );

        if( toupper(takeInChar("Is this entry ok (y/n) ? ")) == 'Y' )
            break;
        /* else ... */
        free( s->lname );
        puts( "Ok ... this entry aborted ... try again ... " );
    }
}

int hasChNotInStr( const char* t, const char* s )
{
    for( ; *t ; ++t )
        if( strchr( s, *t ) == NULL ) return 1;
    /* else ... */
    return 0;
}

int matchList( const Student* m, const Student* t, const char* matchStr )
{
    if( strchr( matchStr, 'N' )) if( strcmp(m->lname, t->lname) != 0 ) return 0;
    if( strchr( matchStr, 'I' )) if( m->id != t->id) return 0;
    if( strchr( matchStr, 'D' )) if( m->dob!= t->dob ) return 0;
    if( strchr( matchStr, 'G' )) if( m->gender != t->gender ) return 0;
    if( strchr( matchStr, 'S' )) if( m->status != t->status ) return 0;
    /* else ...i */
    return 1;
}

/* match string is like this "nidgs" to match on all ... */
void showMatching( const Clist* s )
{
    Student find;
    pNode p;
    const char* valid = "NIDGS";
    char* matchOn;
    int aMatch = 0;
   
    for( ; ; )
    {
        puts( CODES );
        fputs( "Enter 'match criteria string' ... "
               "i.e. all, some or none of 'nidgs' : ", stdout );
        fflush( stdout );
        matchOn = strToUpper(readLine( stdin ));

        if( strcmp( matchOn, "ALL" ) == 0 )
            { free(matchOn); matchOn = newCopy(valid); break; }
        else if( strcmp(matchOn, "NONE" ) == 0 )
            { *matchOn = 0; break; }
        else if( !hasChNotInStr( matchOn, valid ) )
            break;
       
        /* else ... */
        printf( "%s is not valid here ...\n\n", matchOn );
    }
   
    /* printf( "\n\n ... now matchOn = %s \n\n", matchOn ); */
    find.lname = NULL;
    if( strchr( matchOn, 'N' ))
    {
        fputs( "Enter last name to find: ", stdout ); fflush( stdout );
        find.lname = readLine( stdin ); strToUpper( find.lname );
    }
    if( strchr( matchOn, 'I' ))
    {
        find.id = getValidInt( "Enter id to find: " );
    }
    if( strchr( matchOn, 'D' ))
    {
        find.dob = getValidInt( "Enter dob to find: " );
    }
    if( strchr( matchOn, 'G' ))
    {
        find.gender = toupper( takeInChar("Enter gender to find: ") );
    }
    if( strchr( matchOn, 'S' ))
    {
        find.status = toupper( takeInChar("Enter status to find: ") );
    }

    /* Now ... show all records that match all selection criteria ... */
    for( p = s->start; p != NULL; p = p->next )
    {
        if( matchList( p, &find, matchOn ))
        {
            aMatch = 1;
            printf( "%s %d %d %c %c\n",
                     p->lname, p->id, p->dob, p->gender, p->status );
        }
    }
    if( !aMatch ) puts( "No matches ... " );
    putchar( '\n' );
   
    free( matchOn );
    if( find.lname != NULL )
        free( find.lname );
}

void saveToFile( const Clist* myList )
{
    FILE* fout = fopen( FILE_NAME, "w" );
    if( fout )
    {
        pNode i = myList->start;
        for( ; i != NULL; i = i->next )
            fprintf( fout, "%s %d %d %c %c\n",
                     i->lname, i->id, i->dob, i->gender, i->status );
        fclose( fout );
    }
    else
        printf( "There was a problem opening file %s for output.\n", FILE_NAME );
}

void fillFromFile( FILE* fin, Clist* myList )
{
    char nameBuf[80];
    char* line;
    while( (line = readLine(fin)) )
    {
        Student tmp;
        /*
        char *p = line, *q;
        while( *p != ' ' ) ++p;
        *p = 0;
        tmp.lname = newCopy( line );
       
        q = ++p;
        while( *p != ' ' ) ++p;
        *p = 0;
        tmp.id = atoi( q );
       
        q = ++p;
        while( *p != ' ' ) ++p;
        *p = 0;
        tmp.dob = atoi( q );

        tmp.gender = * ++p ;
        ++p;
        tmp.status = * ++p ;
        */
        sscanf( line, "%s %d %d %c %c", nameBuf,
                &tmp.id, &tmp.dob, &tmp.gender, &tmp.status );
        tmp.lname = newCopy( nameBuf );
       
        push_backClist( myList, &tmp );
        free( line );
    }
    fclose( fin );
}


An example of data text file produced/used ...

File name: student.dat
Code: [Select]
JOE 1 19900101 M S
SAM 2 19910101 M F
JIL 3 19900101 F S
SUE 4 19910101 F F
TAM 5 19811203 F G
JUN 6 19890101 F J
TOM 7 19900101 M J

You may like to see a C++ version of the above at this link:
http://developers-heaven.net/forum/index.php/topic,2019.msg2902.html#msg2902
« Last Edit: October 17, 2016, 03:03:08 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #12 on: October 08, 2011, 09:21:04 PM »
This next set of 4+ programs, (especially see phnBook1.c, phnBook2.c, phnBook3.c, phnBook4.c), may help you see the progression from a dynamic array of struct ... to a Cvec of struct using Cvec.h, Cvec_func's.h (and readLine.h re. dynamic C strings) to facilitate coding and code re-use ...

1. Beginning here with a dynamic array of struct with 3 C strings with fixed max length ...

Code: [Select]
/* phnBook1.c */ /* using a dynamic array to hold Contacts ... version 1 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE 1024 /* pick an extra large size, to ensure? large enough ... */
const int BEGIN_SIZE = 8; /* pick begin size to minimize calls to realloc ... */

int SIZE = 0; /* Global variable used to keep track of number of Contacts */
int CAP = 0; /* Global variable used to keep track of capacity for Contacts */

typedef struct myContact
{
    char firstName[21];     /* NOTE!  max num of char's for firstname is 20 */
    char lastName[21];      /* NOTE!  MUST LEAVE ROOM for terminal '\0' char */
    char phoneNumber[11];   /* NOTE!  max num of char's for phoneNumber is 10 */
} Contact ;

Contact* addContact( Contact* );
void deleteContact( Contact* );
void showAllContacts( Contact* );
void sortByFirstName( Contact* );

/* shows 'msg' string and returns right sized C string 'getstr' passed in ... */
void getString( const char* msg, char* getstr, int maxSize, int minSize )
{
    char *p, buffer[BUFSIZE];
    int len;
   
    for( ; ; ) /* loop forever ... until break ... */
    {
        printf( msg );
        fflush( stdout );
        fgets( buffer, BUFSIZE, stdin );
        p = strchr( buffer, '\n' );
        if( p != NULL ) *p = 0; /* eliminate '\n' char at end of C string */
       
        len = strlen(buffer);
        if( len <= maxSize && len >= minSize ) break;
        /* else... */
        printf( "\nERROR! Input must have length in range %d..%d\n",
                minSize, maxSize );
    }

    /* when reach here ... a good len was input ... */
    strcpy(getstr, buffer); /* getstr is retuned 'by ref' ... */
}



int main( void )
{       
    int iSelection = 0, numGood;
    Contact* phonebook = NULL;
   
    printf( "Size of each record = sizeof(Contact) = %d bytes ...\n",
            sizeof(Contact) );

    while( iSelection != 5 )
    {
        printf( "\n\t\t\tContact Menu" );
        printf( "\n\n\t(1)\tAdd Contact" );
        printf( "\n\t(2)\tDelete Contact" );
        printf( "\n\t(3)\tShow All Contacts" );
        printf( "\n\t(4)\tSort All Contacts" );
        printf( "\n\t(5)\tExit ..." );
        printf( "\n\nWhat would you like to do? " );

        numGood = scanf( "%d", &iSelection );
        while( getchar() != '\n' ) ; /* flush stdin ... including '\n' ... */
        if( numGood != 1 )
        {
            printf( "\nEntry ERROR! Please enter an integer ...\n" );
            continue; /* go to top of while loop right now ... */
        }

        if(iSelection == 1)
        {
            phonebook = addContact( phonebook ); /* update address of new memory */
            if( phonebook == NULL )
            {
                printf("\nError getting new memory in addContact ... "
                       "Press 'Enter' to exit ... "); fflush( stdout );
                getchar();
                return -1;
            }
        }
        else if(iSelection == 2) deleteContact( phonebook );
        else if(iSelection == 3) showAllContacts( phonebook );
        else if(iSelection == 4) sortByFirstName( phonebook );
        else if(iSelection == 5)
        {
            printf("\nWill exit now ... Press 'Enter' to continue ... ");
            fflush( stdout );
            getchar();
            free( phonebook );
        }
        else printf( "\n%d NOT implemented yet ...\n", iSelection );
    }
    return 0;
}


Contact* addContact( Contact* phonebook )
{
    void* p;
    if( SIZE == CAP ) /* if needed, reserve new memory to hold next Contact */
    {
        if( CAP != 0 ) CAP += CAP; /* double capacity ... */
        else CAP = BEGIN_SIZE;
       
        p = realloc( phonebook, sizeof(Contact)*CAP );
        if( p == NULL ) { free( phonebook ); return NULL; }
        else phonebook = (Contact*) p; /* update with new address ... */
    }

    getString( "First Name   : ", phonebook[SIZE].firstName, 20, 1 );
    getString( "Last Name    : ", phonebook[SIZE].lastName, 20, 1 );
    getString( "Phone Number : ", phonebook[SIZE].phoneNumber, 10, 10 );

    printf("Contact successfully added ...\n");
    ++SIZE;
    return phonebook; /* return updated address ... */
}
   
void deleteContact( Contact* phonebook )
{
    int i = 0;
    char deleteFirstName[21];
    char deleteLastName[21];

    getString( "First Name   : ", deleteFirstName, 20, 0 );
    getString( "Last Name    : ", deleteLastName, 20, 0 );
   
    for( i = 0; i < SIZE; ++i )
    {
        if( strcmp(deleteFirstName, phonebook[i].firstName) == 0 )
        {
            if( strcmp(deleteLastName, phonebook[i].lastName) == 0 )
            {
                int j;
                for( j = i; j < SIZE-1; ++j ) /* while more data  ... */
                {   /* copy down ... (1st one copied down is over deleted) */
                    strcpy(phonebook[j].firstName, phonebook[j+1].firstName);
                    strcpy(phonebook[j].lastName, phonebook[j+1].lastName);
                    strcpy(phonebook[j].phoneNumber, phonebook[j+1].phoneNumber);
                }
                printf("\nContact deleted ...\n");
                --SIZE; /* update SIZE */
                return; /* exit right now ... */
            }
        }
    }
   
    /* else ... */
    printf("Contact NOT found ...\n"); /* ... and exit void function */
}

void showAllContacts( Contact* phonebook )
{
    int i = 0;
    printf( "\nContacts:\n" );
    for( i = 0; i < SIZE; ++i )
    {
        printf("(%d)\n", i+1);
        printf("Name: %s %s\n", phonebook[i].firstName, phonebook[i].lastName);
        printf("Number: %s\n", phonebook[i].phoneNumber);
    }
    printf( "size = %d, capacity = %d\n", SIZE, CAP );
}

void sortByFirstName( Contact* phonebook )
{
    int i, swap, size = SIZE;
    do
    {
        swap = 0;
        for( i = 1; i < size; ++i )
        {
            if( strcmp(phonebook[i-1].firstName, phonebook[i].firstName) > 0 )
            {
                Contact tmp;
                memcpy( &tmp, &phonebook[i], sizeof(Contact) );
                memcpy( &phonebook[i], &phonebook[i-1], sizeof(Contact) );
                memcpy( &phonebook[i-1], &tmp, sizeof(Contact) );
                /*
                tmp = phonebook[i];
                phonebook[i] = phonebook[i-1];
                phonebook[i-1] = tmp;
                */
               
                /*
                strcpy(tmp.firstName, phonebook[i].firstName);
                strcpy(tmp.lastName, phonebook[i].lastName);
                strcpy(tmp.phoneNumber, phonebook[i].phoneNumber);

               
                strcpy(phonebook[i].firstName, phonebook[i-1].firstName);
                strcpy(phonebook[i].lastName, phonebook[i-1].lastName);
                strcpy(phonebook[i].phoneNumber, phonebook[i-1].phoneNumber);
               
                strcpy(phonebook[i-1].firstName, tmp.firstName);
                strcpy(phonebook[i-1].lastName, tmp.lastName);
                strcpy(phonebook[i-1].phoneNumber, tmp.phoneNumber);
                */
                swap = 1;
            }
        }
        --size;
    }while( swap );
    printf( "Contacts sorted now ...\n" );
}


Same as 1. above but ... demos using an array of pointers to struct ...
(just to show how it might be done and to speed up sorts and swaps by only needing to swap pointers to struct)

Code: [Select]
/* phnBook1_aryPtrs.c */ /* using a dynamic array of pointers to each new Contact
                            ... version 1_aryPtrs  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE 1024 /* pick an extra large size, to ensure? large enough ... */
const int BEGIN_SIZE = 8; /* pick begin size to minimize calls to realloc ... */

int SIZE = 0; /* Global variable used to keep track of number of Contacts */
int CAP = 0; /* Global variable used to keep track of capacity for Contacts */

typedef struct myContact
{
    char firstName[21];     /* NOTE!  max num of char's for firstname is 20 */
    char lastName[21];      /* NOTE!  MUST LEAVE ROOM for terminal '\0' char */
    char phoneNumber[11];   /* NOTE!  max num of char's for phoneNumber is 10 */
} Contact ;

Contact** addContact( Contact** );
void deleteContact( Contact** );
void showAllContacts( Contact** );
void sortByFirstName( Contact** );

Contact** freeContacts( Contact** );

/* shows 'msg' string and returns right sized C string 'getstr' passed in ... */
void getString( const char* msg, char* getstr, int maxSize, int minSize )
{
    char *p, buffer[BUFSIZE];
    int len;
   
    for( ; ; ) /* loop forever ... until break ... */
    {
        printf( msg );
        fflush( stdout );
        fgets( buffer, BUFSIZE, stdin );
        p = strchr( buffer, '\n' );
        if( p != NULL ) *p = 0; /* eliminate '\n' char at end of C string */
       
        len = strlen(buffer);
        if( len <= maxSize && len >= minSize ) break;
        /* else... */
        printf( "\nERROR! Input must have length in range %d..%d\n",
                minSize, maxSize );
    }

    /* when reach here ... a good len was input ... */
    strcpy(getstr, buffer); /* getstr is retuned 'by ref' ... */
}



int main( void )
{       
    int iSelection = 0, numGood;
    Contact** phonebook = NULL;
   
    printf( "Size of each record = sizeof(Contact) = %d bytes ...\n",
            sizeof(Contact) );
           
    printf( "Size of pointer to each record = sizeof(Contact*) = %d bytes ...\n",
            sizeof(Contact*) );
           
    while( iSelection != 5 )
    {
        printf( "\n\t\t\tContact Menu" );
        printf( "\n\n\t(1)\tAdd Contact" );
        printf( "\n\t(2)\tDelete Contact" );
        printf( "\n\t(3)\tShow All Contacts" );
        printf( "\n\t(4)\tSort All Contacts" );
        printf( "\n\t(5)\tExit ..." );
        printf( "\n\nWhat would you like to do? " );

        numGood = scanf( "%d", &iSelection );
        while( getchar() != '\n' ) ; /* flush stdin ... including '\n' ... */
        if( numGood != 1 )
        {
            printf( "\nEntry ERROR! Please enter an integer ... \n" );
            continue; /* go to top of while loop right now ... */
        }

        if(iSelection == 1)
        {
            phonebook = addContact( phonebook ); /* update address of new memory */
            if( phonebook == NULL )
            {
                printf("\nError getting new memory in addContact ... "
                       "Press 'Enter' to exit ... "); fflush( stdout );
                getchar();
                return -1;
            }
        }
        else if(iSelection == 2) deleteContact( phonebook );
        else if(iSelection == 3) showAllContacts( phonebook );
        else if(iSelection == 4) sortByFirstName( phonebook );
        else if(iSelection == 5)
        {
            printf("\nWill exit now ... Press 'Enter' to continue ... ");
            fflush( stdout );
            getchar();
            phonebook = freeContacts( phonebook );
        }
        else printf( "\n%d NOT implemented yet ...\n", iSelection );
    }
    return 0;
}


Contact** addContact( Contact** phonebook )
{
    void* p;
    if( SIZE == CAP ) /* if needed,
                         reserve new memory to hold next pointer to Contact */
    {
        if( CAP != 0 ) CAP += CAP; /* double capacity ... */
        else CAP = BEGIN_SIZE;
       
        p = realloc( phonebook, sizeof(Contact*)*CAP );
        if( p == NULL ) { freeContacts( phonebook ); return NULL; }
        else phonebook = (Contact**) p; /* update with new address ... */
    }

    /* get pointer (and new memory) for this contact */
    phonebook[SIZE] = malloc( sizeof(Contact) );
    if( phonebook[SIZE] == NULL ) { freeContacts( phonebook ); return NULL; }
   
    getString( "First Name   : ", phonebook[SIZE]->firstName, 20, 1 );
    getString( "Last Name    : ", phonebook[SIZE]->lastName, 20, 1 );
    getString( "Phone Number : ", phonebook[SIZE]->phoneNumber, 10, 10 );

    printf("Contact successfully added ...\n");
    ++SIZE;
    return phonebook; /* return updated address ... */
}
   
void deleteContact( Contact** phonebook )
{
    int i = 0;
    char deleteFirstName[21];
    char deleteLastName[21];

    getString( "First Name   : ", deleteFirstName, 20, 0 );
    getString( "Last Name    : ", deleteLastName, 20, 0 );
    for( i = 0; i < SIZE; ++i )
    {
        if( strcmp(deleteFirstName, phonebook[i]->firstName) == 0 )
        {
            if( strcmp(deleteLastName, phonebook[i]->lastName) == 0 )
            {
                int j;
                free( phonebook[i] ); /* free this memory block first ... */
                for( j = i; j < SIZE-1; ++j ) /* while more data  ... */
                { /* copy down (1st pointer copied down is over deleted ... ) */
                    phonebook[j] = phonebook[j+1];
                }
                printf("Contact deleted ...\n");
                --SIZE; /* now update SIZE ... */
                return; /* and exit right now ... */
            }
        }
    }
   
    /* else ... */
    printf("Contact NOT found ...\n"); /* ... and exit void function */
}

void showAllContacts( Contact** phonebook )
{
    int i = 0;
    printf( "\nContacts:\n" );
    for( i = 0; i < SIZE; ++i )
    {
        printf("(%d)\n", i+1);
        printf("Name: %s %s\n", phonebook[i]->firstName, phonebook[i]->lastName);
        printf("Number: %s\n", phonebook[i]->phoneNumber);
    }
    printf( "size = %d, capacity = %d\n", SIZE, CAP );
}

void sortByFirstName( Contact** phonebook )
{
    int i, swap, size = SIZE;
    do
    {
        swap = 0;
        for( i = 1; i < size; ++i )
        {
            if( strcmp(phonebook[i-1]->firstName, phonebook[i]->firstName) > 0 )
            {
                Contact* tmp = phonebook[i];        /* swap pointers ... */
                phonebook[i] = phonebook[i-1];
                phonebook[i-1] = tmp;
                swap = 1;
            }
        }
        --size;
    }while( swap );
    printf( "Contacts sorted now ...\n" );
}

Contact** freeContacts( Contact** c )
{
    --SIZE;
    for( ; SIZE > 0; --SIZE ) free( c[SIZE] ); /* first ... free all these */
    free( c ); /* now free memory for array to hold pointers to each Contact */
    CAP = SIZE = 0; /* recall: SIZE and CAP are Globals ... */
    return NULL;
}
« Last Edit: January 07, 2012, 05:53:38 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #13 on: October 08, 2011, 09:28:09 PM »
Same as 1. above ... but with functions added for edit, sort and file ...

Code: [Select]
/* phnBook1_withfile.c */ /* using a dynamic array to hold Contacts ... version 1 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> /* re. tolower ... */

#define FNAME "myContacts.txt" /* default file name ... */

#define MENU "\nContact Menu ...\n"  \
             "\t(1)\tAdd Contact\n" \
             "\t(2)\tEdit/Delete Contact\n" \
             "\t(3)\tShow All Contacts\n" \
             "\t(4)\tsOrt All Contacts\n" \
             "\t(5)\tFile All Contacts\n" \
             "\t(6)\tLoad Contacts from file\n" \
             "\t(7)\teXit ...\n" \
             "Please enter a character from 1..7 or A,E,D,S,O,F,L,X : "

#define BUFSIZE 1024 /* pick an extra large size, to ensure? large enough ... */
const int BEGIN_SIZE = 1; /* BUT set BEGIN_SIZE to minimize calls to realloc */

int SIZE = 0; /* Global variable used to keep track of number of Contacts */
int CAP = 0; /* Global variable used to keep track of capacity for Contacts */

typedef struct myContact
{
    char firstName[21];     /* NOTE!  max num of char's for firstname is 20 */
    char lastName[21];      /* NOTE!  MUST LEAVE ROOM for terminal '\0' char */
    char phoneNumber[11];   /* NOTE!  max num of char's for phoneNumber is 10 */
} Contact ;

int fileContacts( const char*, Contact* );
Contact* loadContacts( const char*, Contact* );
Contact* addContact( Contact* );
void editDeleteContact( Contact*, char );
void printFormatted( const char* phone );
void showAllContacts( const Contact* );

int cmpNames( const Contact*, const Contact* );
void sortByNames( Contact* );
int cmpPhones( const Contact*, const Contact* );
void sortByPhones( Contact* );

int getChar( const char* msg );
void getFileName( char* filename );
int fileExists( const char* filename );

/* get a string from keyboard of length minSize to maxSize characters */
/* show 'msg' string and return right sized C string 'getstr' passed in  */
void getString( const char* msg, char* getstr, int maxSize, int minSize );

/* get ALL line from file, and return all char's upto maxSize length in inStr */
/* 'safe fix to using fgets' to read a whole line less end '\n' char if exists */
/* Note: if input string exceeds maxSize-1 characters ... it gets truncated */
char* myFgets( char* inStr, int maxSize, FILE* fin );




int main( void ) /* ****************** BEGIN MAIN *************************** */
{       
    Contact* c = NULL;
    char filename[BUFSIZE];
    int reply = 'y';
   
    printf( "Size of each record = sizeof(Contact) = %d bytes ...\n",
            sizeof(Contact) );

    while( !( reply == '7' || reply == 'x') )
    {
        printf( MENU ); fflush( stdout );
        reply = getChar( "" );

        if(reply == '1' || reply == 'a')
        {
            c = addContact( c ); /* update address of new memory */
            if( c == NULL )
            {
                printf("\nError getting new memory in addContact ... "
                       "Press 'Enter' to exit ... "); fflush( stdout );
                getchar();
                return -1;
            }
        }
        else if(reply == '2' || reply == 'e' || reply == 'd')
            editDeleteContact( c, reply );
        else if(reply == '3' || reply == 's')
            showAllContacts( c );
        else if(reply == '4' || reply == 'o')
        {
            int ans = getChar( "sOrt by names or phone (n/p) ? " );
            if( ans == 'n' ) sortByNames( c );
            else if( ans == 'p' ) sortByPhones( c );
            else puts( "You didn't enter either 'n' or 'p' ..." );
        }
        else if(reply == '5' || reply == 'f')
        {
            getFileName( filename );
            if( fileExists( filename ) )
            {
                int ans = getChar("Do you want to overwrite that file (y/n) ? ");
                if( ans == 'y' )
                {
                    int count = fileContacts( filename, c );
                    if( count == SIZE )
                        printf( "All %d records in the array were filed to",
                                count );
                    else
                    {
                        printf
                        (
                            "An error occured! "
                            "Only %d of %d records in the array were filed to",
                            count, SIZE
                        );
                        reply = 'x';
                    }
                    printf( " '%s'\n", filename );
                }
                else puts( "File write aborted ..." );
            }
            else
            {
                int count = fileContacts( filename, c );
                if( count == SIZE )
                    printf( "All %d records in the array were filed to",
                            count);
                else
                {
                    printf
                    (
                        "An error occured! "
                        "Only %d of %d records in the array were filed to",
                        count, SIZE
                    );
                    reply = 'x';
                }
                printf( " '%s'\n", filename );
            }
        }
        else if(reply == '6' || reply == 'l')
        {
            getFileName( filename );
            if( fileExists(filename) )
            {
                int ans;
                if( SIZE )
                {
                    ans = getChar( "Do you want to append/overwrite "
                                        "the array in memory (a/o) ? ");
                    if( ans == 'o' )
                    {
                        free( c );
                        SIZE = CAP = 0;
                        c = loadContacts( filename, c );
                    }
                    else if( ans == 'a' )
                    {
                        puts( "Ok ... will append to array ..." );
                        c = loadContacts( filename, c );
                    }
                    else puts( "You didn't enter either of 'A' or 'O' ... " );
                }
                else c = loadContacts( filename, c );
               
                if( c && (ans == 'a' || ans == 'o') )
                    printf( "%d records are now in array memory.", SIZE );
                else if( ans == 'a' || ans == 'o' )
                {
                    puts( "There was an error allocating memory ..." );
                    reply = 'x';
                }
            }
            else
            {
                printf( "The file '%s' does not exist yet ...\n", filename );
                puts( "Select choice 'File All Contacts' "
                      "after you have added some contacts." );
            }
        }
        else if(reply == '7' || reply == 'x' )
        {
            if( getChar("Do you really want to eXit (y/n) ? ") == 'y' )
            {
                if( getChar("\nWill eXit now ... "
                            "Press 'Enter' to continue/Enter 'a' to abort ... ")
                            == 'a' )
                {
                    reply = 'y';
                    puts( "Exit aborted ..." );
                }
                else free( c );
            }
            else
            {
                reply = 'y';
                puts( "Exit aborted ..." );
            }
               
        }
        else if( reply != '\n' )
            printf( "\nChoice '%c' NOT implemented yet ...\n", reply );
    }
    return 0;
} /* ***************************** END MAIN ********************************* */



int fileContacts( const char* filename, Contact* c )
{
    FILE* fout = fopen( filename, "w" );
    if( fout )
    {
        int i = 0;
        for( i = 0; i < SIZE; ++i )
            fprintf
            (
                fout,
                "%s\n%s\n%s\n",
                c[i].firstName, c[i].lastName, c[i].phoneNumber
            );
        fclose( fout );
        return i;
    }
    /* else ... */
    printf( "\nThere was an error trying to open file '%s' to write ...\n",
            filename );
    return 0;
}

Contact* loadContacts( const char* filename, Contact* c )
{
    FILE* fin = fopen( filename, "r" );
    if( fin )
    {
        int i = 0;
        Contact tmp;
        while
        (
            myFgets( tmp.firstName, sizeof(tmp.firstName), fin ) &&
            myFgets( tmp.lastName, sizeof(tmp.lastName), fin ) &&
            myFgets( tmp.phoneNumber, sizeof(tmp.phoneNumber), fin )
        )
        {
            void* p;
            if( SIZE == CAP ) /* if needed, reserve new memory to hold next Contact */
            {
                if( CAP != 0 ) CAP += CAP; /* double capacity ... */
                else CAP = BEGIN_SIZE;

                p = realloc( c, sizeof(Contact)*CAP );
                if( p == NULL ) { free( c ); return NULL; }
                else c = (Contact*) p; /* update with new address ... */
            }
           
            strcpy( c[SIZE].firstName, tmp.firstName );
            strcpy( c[SIZE].lastName, tmp.lastName );
            strcpy( c[SIZE].phoneNumber, tmp.phoneNumber );
            ++SIZE;
            ++i;
        }
        fclose( fin );
        printf( "\n%d records were read from file %s ...\n", i, filename );
        return c; /* return updated address ... */
    }
    /* else ... */
    return NULL;
}

Contact* addContact( Contact* c )
{
    void* p;
    if( SIZE == CAP ) /* if needed, reserve new memory to hold next Contact */
    {
        if( CAP != 0 ) CAP += CAP; /* double capacity ... */
        else CAP = BEGIN_SIZE;
       
        p = realloc( c, sizeof(Contact)*CAP );
        if( p == NULL ) { free( c ); return NULL; }
        else c = (Contact*) p; /* update with new address ... */
    }

    getString( "First Name   : ", c[SIZE].firstName, 20, 1 );
    getString( "Last Name    : ", c[SIZE].lastName, 20, 1 );
    getString( "Phone Number : ", c[SIZE].phoneNumber, 10, 10 );
   
    if( getChar("Ok to add (y/n) ? ") != 'y' )
    {
        puts( "Not added ..." );
        return c;
    }

    printf("Contact successfully added ...\n");
    ++SIZE;
    return c; /* return updated address ... */
}
   
void editDeleteContact( Contact* c, char reply )
{
    int i = 0;
    Contact ed;
    if( reply == '2' ) reply = getChar("Edit or Delete (e/d) ? ");
   
    puts( "Enter an 'empty' record to exit ..." );

    getString( "First Name   : ", ed.firstName, 20, 0 );
    getString( "Last Name    : ", ed.lastName, 20, 0 );
    getString( "Phone Number : ", ed.phoneNumber, 10, 0 );
   
    for( i = 0; i < SIZE; ++i )
    {
        if
        (
            strcmp(ed.firstName, c[i].firstName) == 0 &&
            strcmp(ed.lastName, c[i].lastName) == 0 &&
            strcmp(ed.phoneNumber, c[i].phoneNumber)==0
        )
        {
            if( reply == 'd' &&
                getChar("Do you really want to delete (y/n) ? ") == 'y' )
            {
                int j;
                for( j = i; j < SIZE-1; ++j ) /* while more data  ... */
                {   /* copy down ... (1st one copied down is over deleted) */
                    strcpy(c[j].firstName, c[j+1].firstName);
                    strcpy(c[j].lastName, c[j+1].lastName);
                    strcpy(c[j].phoneNumber, c[j+1].phoneNumber);
                }
                printf("\nContact deleted ...\n");
                --SIZE; /* update SIZE */
                return; /* exit right now */
            }
            else if( reply == 'e' &&
                     getChar("Do you really want to edit (y/n) ? ") == 'y' )
            {
                getString( "First Name   : ", ed.firstName, 20, 1 );
                getString( "Last Name    : ", ed.lastName, 20, 1 );
                getString( "Phone number : ", ed.phoneNumber, 10, 10 );
               
                if( getChar( "Ok to change (y/n) ? " ) == 'y' )
                {
                    strcpy(c[i].firstName, ed.firstName);
                    strcpy(c[i].lastName, ed.lastName);
                    strcpy(c[i].phoneNumber, ed.phoneNumber);
                    return; /* exit right now */
                }
                else { puts("Aborted ..."); return; } /* exit right now */
            }
            else { puts("Aborted ..."); return; } /* exit right now */
        }
    }
   
    /* else ... */
    printf("Contact NOT found ...\n"); /* ... and exit void function */
}

void printFormatted( const char* phone )
{
    char* p = "xxx-xxx-xxxx";
    for( ; *p != 0; ++p )
    {
        if( *p == 'x' ) putchar( *phone++ );
        else putchar( *p );
    }
}

void showAllContacts( const Contact* c )
{
    int i = 0;
    printf( "\nContacts:\n" );
    for( i = 0; i < SIZE; ++i )
    {
        printf("(%2d) ", i+1);
        printFormatted( c[i].phoneNumber );
        printf(" : %s, %s\n",c[i].lastName, c[i].firstName);
    }
    printf( "size = %d, capacity = %d\n", SIZE, CAP );
}

int cmpNames( const Contact* a, const Contact* b )
{
    int last = strcmp( a->lastName, b->lastName );
    if( last == 0 )
    {
        int first = strcmp( a->firstName, b->firstName );
        if( first == 0 ) return strcmp( a->phoneNumber, b->phoneNumber );
        else return first;
    }
    else return last;
}

int cmpPhones( const Contact* a, const Contact* b )
{
    int phone = strcmp( a->phoneNumber, b->phoneNumber );
    if( phone == 0 )
    {
        int last = strcmp( a->lastName, b->lastName );
        if( last == 0 ) return strcmp( a->firstName, b->firstName );
        else return last;
    }
    else return phone;
}

/* a simple example of a bubble sort is coded here ... */
void sortByNames( Contact* c )
{
    int i, swap, size = SIZE; /* recall that SIZE is a global variable */
    Contact tmp;
    do
    {
        swap = 0;
        for( i = 1; i < size; ++i )
        {
            if( cmpNames(&c[i-1], &c[i]) > 0 )
            {
                memcpy( &tmp, &c[i], sizeof(Contact) );
                memcpy( &c[i], &c[i-1], sizeof(Contact) );
                memcpy( &c[i-1], &tmp, sizeof(Contact) );
                swap = 1;
               
                /* // or swap like this ... //
                tmp = c[i];
                c[i] = c[i-1];
                c[i-1] = tmp;
                */
               
                /* // or swap like this ... //
                strcpy(tmp.firstName, c[i].firstName);
                strcpy(tmp.lastName, c[i].lastName);
                strcpy(tmp.phoneNumber, c[i].phoneNumber);

                strcpy(c[i].firstName, c[i-1].firstName);
                strcpy(c[i].lastName, c[i-1].lastName);
                strcpy(c[i].phoneNumber, c[i-1].phoneNumber);
               
                strcpy(c[i-1].firstName, tmp.firstName);
                strcpy(c[i-1].lastName, tmp.lastName);
                strcpy(c[i-1].phoneNumber, tmp.phoneNumber);
                */
            }
        }
        --size;
    }while( swap );
    printf( "Contacts sorted by last name, first name, phone number now ...\n" );
}

/* a simple example of a bubble sort is coded here ... */
void sortByPhones( Contact* c )
{
    int i, swap, size = SIZE; /* recall that SIZE is a global variable */
    Contact tmp;
    do
    {
        swap = 0;
        for( i = 1; i < size; ++i )
        {
            if( cmpPhones(&c[i-1], &c[i]) > 0 )
            {
                memcpy( &tmp, &c[i], sizeof(Contact) );
                memcpy( &c[i], &c[i-1], sizeof(Contact) );
                memcpy( &c[i-1], &tmp, sizeof(Contact) );
                swap = 1;
            }
        }
        --size;
    }while( swap );
    printf( "Contacts sorted by phone number, last name, first name now ...\n" );
}

int getChar( const char* msg )
{
    int c, reply;
    printf( "%s", msg ); fflush( stdout );
    c = reply = getchar();
    while( c != '\n' ) c = getchar(); /* 'flush' stdin ... */
    return tolower( reply );
}

void getFileName( char* filename )
{
    if( getChar("Use default file or input file name (d/i) ? ") == 'd' )
        strcpy( filename, FNAME );
    else
    {
        printf( "Input file name to use: " ); fflush( stdout );
        myFgets( filename, BUFSIZE, stdin );
    }
}

int fileExists( const char* filename )
{
    FILE* f = fopen( filename, "r" );
    if( f )
    {
        fclose( f );
        return 1;
    }
    /* else ... */
    return 0;
}

/* get a string from keyboard of length minSize to maxSize characters */
/* show 'msg' string and return right sized C string 'getstr' passed in  */
void getString( const char* msg, char* getstr, int maxSize, int minSize )
{
    char *p, buffer[BUFSIZE];
    int len;

    for( ; ; ) /* loop forever ... until break ... */
    {
        printf( msg );
        fflush( stdout );
        fgets( buffer, BUFSIZE, stdin );
        p = strchr( buffer, '\n' );
        if( p != NULL ) *p = 0; /* eliminate '\n' char at end of C string */

        len = strlen(buffer);
        if( len <= maxSize && len >= minSize ) break;
        /* else... */
        printf( "\nERROR! Length was %d ... Valid length is in range %d..%d\n",
                len, minSize, maxSize );
    }

    /* when reach here ... a good len was input ... */
    strcpy(getstr, buffer); /* getstr is retuned 'by ref' ... */
}

/* get ALL line from file, and return all char's upto maxSize length in inStr */
/* 'safe fix to using fgets' to read a whole line less end '\n' char if exists */
/* Note: if input string exceeds maxSize-1 characters ... it gets truncated */
char* myFgets( char* inStr, int maxSize, FILE* fin )
{
    char myBuf[BUFSIZE] = { 0 }; /* fill with '\0' char's ... */
    if( fgets(myBuf, BUFSIZE, fin) ) /* i.e. fgets(myBuf, BUFSIZE, fin) != NULL */
    {
        char* p = strchr( myBuf, '\n' );
        if( p ) *p = 0; /* eliminate '\n' char at end of C string, if exists */
        else /* 'flush' any remaining char's in this extra long input string */
        {
            int c = fgetc(fin);
            while( c != '\n' && c != EOF ) c = fgetc(fin);
        }

        strncpy(inStr, myBuf, maxSize); /* now only copy first maxSize char's */
        if( inStr[maxSize-1] != 0 ) inStr[maxSize-1] = 0; /* inStr is truncated */
        return inStr;
    }
    else return NULL;
}
« Last Edit: October 29, 2011, 07:38:03 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: Six Fast Steps to Programming in C
« Reply #14 on: October 08, 2011, 09:32:25 PM »
2.  A next step using dynamic C strings in your struct ...

Code: [Select]
/* phnBook2.c */ /* using dynamic C strings in each Contact ... version 2 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE 1024 /* pick an extra large size, to ensure? large enough ... */

#define MENU "Contact Menu ...\n" \
             "\t(1)\tAdd Contact\n" \
             "\t(2)\tDelete Contact\n" \
             "\t(3)\tShow All Contacts\n" \
             "\t(4)\tExit ...\n" \
             "What would you like to do? "
             
const int BEGIN_SIZE = 8; /* pick begin size to minimize calls to realloc ... */

int SIZE = 0; /* Global variable used to keep track of number of Contacts */
int CAP = 0; /* Global variable used to keep track of capacity for Contacts */

typedef struct myContact
{
    char* firstName;
    char* lastName;
    char* phoneNumber; /* num of char's for phoneNumber is 10 + terminal 0 */
} Contact ;

Contact* addContact( Contact* );
void deleteContact( Contact* );
void showAllContacts( const Contact* );

char* newCopy( const char* s )
{
    char* p = malloc( strlen(s) + 1 );
    if( p == NULL )
    {
        printf( "\nERROR! malloc failed in 'newCopy' ... " );
        printf( "\nPress 'Enter' to exit ... " );
        fflush( stdout );
        getchar();
        exit(1);
    }
    /* else ... if reach here ... memory was allocated ... */
    strcpy( p, s );
    return p; /* return address to new memory ... */
}

char* getString( const char* msg, int maxSize, int minSize )
{
    char *p, buffer[BUFSIZE];
    int len;
   
    for( ; ; ) /* loop forever ... until break ... */
    {
        printf( msg );
        fflush( stdin );
        fgets( buffer, BUFSIZE, stdin );
        p = strchr( buffer, '\n' );
        if( p != NULL ) *p = 0; /* eliminate '\n' char at end of C string */
       
        len = strlen(buffer);
        if( len <= maxSize && len >= minSize ) break;
        /* else... */
        printf( "\nERROR! Input must have length in range %d..%d\n",
                minSize, maxSize );
    }

    /* when reach here ... a good len was input ... */
    return newCopy(buffer);
}



int main( void )
{       
    int i, iSelection = 0, numGood;
    Contact* phonebook = NULL;
   
    printf( "Size of each record = sizeof(Contact) = %d bytes ...\n", sizeof(Contact) );

    while( iSelection != 4 )
    {
        printf( MENU );
        fflush( stdout );
        numGood = scanf( "%d", &iSelection );
        while( getchar() != '\n' ) ; /* flush stdin ... */
       
        if( numGood != 1 )
        {
            printf( "\nEntry ERROR! Please enter an integer in range 1..4\n" );
            continue; /* goto top of while loop right now ... */
        }

        if( iSelection == 1 )
        {
            phonebook = addContact(phonebook); /* update address of new memory */
            if( phonebook == NULL )
            {
                printf( "\nError getting new memory in addContact ... "
                        "Press 'Enter' to exit ... " );
                fflush( stdout );
                getchar();
                return -1;
            }
        }
        else if( iSelection == 2 ) deleteContact(phonebook);
        else if( iSelection == 3 ) showAllContacts(phonebook);
        else if( iSelection == 4 )
        {
            printf( "\nWill exit now ...\n" );
            for( i = SIZE-1; i >= 0; --i )
            {
                free( phonebook[i].phoneNumber );
                free( phonebook[i].lastName );
                free( phonebook[i].firstName );
            }
            free(phonebook);
        }
        else printf( "\n%d NOT implemented yet ...\n", iSelection );
    }
    return 0;
}


Contact* addContact( Contact* phonebook )
{
    void* p;
    if( SIZE == CAP )
    {
        if( CAP != 0 ) CAP += CAP; /* double capacity ... */
        else CAP = BEGIN_SIZE;
       
        p = realloc( phonebook, sizeof(Contact)*CAP );
        if( p == NULL ) { free(phonebook); return NULL; }
        else phonebook = (Contact*) p; /* update with new address ... */
    }

    /* NOTE: just pointers to new copy of C string are copied below ... */
    phonebook[SIZE].firstName = getString( "First Name   : ", 100, 1 );
    phonebook[SIZE].lastName = getString( "Last Name    : ", 100, 1 );
    phonebook[SIZE].phoneNumber = getString( "Phone Number : ", 10, 10 );

    printf( "\nContact successfully added ...\n" );

    ++SIZE;
    return phonebook; /* return updated address ... */
}
   
void deleteContact( Contact* phonebook )
{
    int x = 0;
    char* deleteFirstName;
    char* deleteLastName;

    /* NOTE: just pointers to new copy of C string are copied below ... */
    deleteFirstName = getString( "First Name   : ", 100, 0 );
    deleteLastName = getString( "Last Name    : ", 100, 0 );
   
    for( x = 0; x < SIZE; ++x )
    {
        if( strcmp(deleteFirstName, phonebook[x].firstName) == 0 )
        {
            if( strcmp(deleteLastName, phonebook[x].lastName) == 0 )
            {
                int i = x ;
                free( phonebook[i].phoneNumber ); /* free dynamic C string */
                free( phonebook[i].lastName );
                free( phonebook[i].firstName );
                for( ; i < SIZE-1; ++i )
                {   /* copy pointers down ... */
                    phonebook[i].firstName = phonebook[i+1].firstName;
                    phonebook[i].lastName = phonebook[i+1].lastName;
                    phonebook[i].phoneNumber = phonebook[i+1].phoneNumber;
                }
                printf( "\nContact deleted from Array of Contacts ...\n" );
                --SIZE;
                free( deleteLastName ); /* free all tmp memory used here ... */
                free( deleteFirstName );
                return;
            }
        }
    }
   
    /* else ... */
    printf("\nContact NOT found ...\n\n");
    free(deleteLastName); /* free all tmp memory used here ... */
    free(deleteFirstName);
}

void showAllContacts( const Contact* phonebook )
{
    int x = 0;
    printf( "\nContacts:\n" );
    for( x = 0; x < SIZE; ++x )
    {
        printf( "(%d)\n", x+1);
        printf( "Name: %s %s\n", phonebook[x].firstName, phonebook[x].lastName );
        printf( "Number: %s\n", phonebook[x].phoneNumber );
    }
    printf( "size = %d, capacity = %d\n", SIZE, CAP );
}


3. A 3rd step ... that eliminates using global variables by holding your dynamic array's size and capacity in a 'struct of struct' ...

Code: [Select]
/* phnBook3.c */ /* using Cvec, a struct of struct,
                    to eliminate global variables */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE 1024 /* pick an extra large size, to ensure? large enough ... */

#define MENU "Contact Menu ...\n" \
             "\t(1)\tAdd Contact\n" \
             "\t(2)\tDelete Contact\n" \
             "\t(3)\tShow All Contacts\n" \
             "\t(4)\tExit ...\n" \
             "What would you like to do? "
             
const int BEGIN_SIZE = 8; /* pick begin size to minimize calls to realloc */

typedef struct
{
    char* firstName;
    char* lastName;
    char* phoneNumber; /* num of char's for phoneNumber is 10 + terminal 0 */
} Contact ;

void freeContact( Contact* c )
{
    free( c->phoneNumber );
    free( c->lastName );
    free( c->firstName );
}

typedef struct
{
    Contact* ary;
    int size;
    int cap;
} Cvec;

void clearCvec( Cvec* cv )
{
    int i;
    for( i = cv->size-1; i >= 0; --i ) freeContact( &cv->ary[i] );
    free( cv->ary );
    cv->size = cv->cap = 0;
}

void addContact( Cvec* );
void deleteContact( Cvec* );
void showAllContacts( const Cvec* );

char* newCopy( const char* s )
{
    char* p = malloc( strlen(s) + 1 );
    if( p == NULL )
    {
        printf( "\nERROR! malloc failed in 'newCopy' ... " );
        printf( "\nPress 'Enter' to exit ... " );
        fflush( stdout );
        getchar();
        exit(1);
    }
    /* else ... if reach here ... memory was allocated ... */
    strcpy( p, s );
    return p; /* return address to new memory ... */
}

char* getString( const char* msg, int maxSize, int minSize )
{
    char *p, buffer[BUFSIZE];
    int len;
   
    for( ; ; ) /* loop forever ... until break ... */
    {
        printf( msg );
        fflush( stdin );
        fgets( buffer, BUFSIZE, stdin );
        p = strchr( buffer, '\n' );
        if( p != NULL ) *p = 0; /* eliminate '\n' char at end of C string */
       
        len = strlen(buffer);
        if( len <= maxSize && len >= minSize ) break;
        /* else... */
        printf( "\nERROR! Input must have length in range %d..%d\n",
                minSize, maxSize );
    }

    /* when reach here ... a good len was input ... */
    return newCopy(buffer);
}



int main( void )
{       
    int iSelection = 0, numGood;
    Cvec cv;
    cv.size = cv.cap = 0; /* BOTH NEED to be '0' to start dynamic array ok */
   
   
    printf( "Size of each record = sizeof(Contact) = %d bytes ...\n", sizeof(Contact) );

    while( iSelection != 4 )
    {
        printf( MENU );
        fflush( stdout );
        numGood = scanf( "%d", &iSelection );
        while( getchar() != '\n' ) ; /* flush stdin ... */
       
        if( numGood != 1 )
        {
            printf( "\nEntry ERROR! Please enter an integer in range 1..4\n" );
            continue; /* goto top of while loop right now ... */
        }

        if( iSelection == 1 )
        {
            addContact(&cv); /* updates address of new memory */
            if( cv.ary == NULL )
            {
                printf( "\nError getting new memory in addContact ... "
                        "Press 'Enter' to exit ... " );
                fflush( stdout );
                getchar();
                return -1;
            }
        }
        else if( iSelection == 2 ) deleteContact(&cv); /* updates cv */
        else if( iSelection == 3 ) showAllContacts(&cv); /* passes address */
        else if( iSelection == 4 )
        {
            printf( "\nWill exit now ...\n" );
            clearCvec( &cv );
        }
        else printf( "\n%d NOT implemented yet ...\n", iSelection );
    }
    return 0;
}


void addContact( Cvec* cv )
{
    void* p;
    if( cv->size == cv->cap )
    {
        if( cv->cap != 0 ) cv->cap += cv->cap; /* double capacity ... */
        else cv->cap = BEGIN_SIZE;
       
        p = realloc(cv->ary, sizeof(Contact) * (cv->cap));
        if( p == NULL ) { clearCvec(cv); cv->ary = NULL; return; }
        else cv->ary = (Contact*) p; /* update with new address ... */
    }

    cv->ary[cv->size].firstName = getString( "First Name   : ", 100, 1 );
    cv->ary[cv->size].lastName = getString( "Last Name    : ", 100, 1 );
    cv->ary[cv->size].phoneNumber = getString( "Phone Number : ", 10, 10 );

    printf( "\nContact successfully added ...\n" );

    ++ cv->size;
}
   
void deleteContact( Cvec* cv )
{
    int x = 0;
    char* deleteFirstName;
    char* deleteLastName;

    deleteFirstName = getString( "First Name   : ", 100, 0 );
    deleteLastName = getString( "Last Name    : ", 100, 0 );
   
    for( x = 0; x < cv->size; ++x )
    {
        if( strcmp(deleteFirstName, cv->ary[x].firstName) == 0 )
        {
            if( strcmp(deleteLastName, cv->ary[x].lastName) == 0 )
            {
                int i = x ;
                freeContact( &cv->ary[i] );
                for( ; i < cv->size-1; ++i )
                {   /* copy pointers down ... */
                /*
                    cv->ary[i].firstName = cv->ary[i+1].firstName;
                    cv->ary[i].lastName = cv->ary[i+1].lastName;
                    cv->ary[i].phoneNumber = cv->ary[i+1].phoneNumber;
                */
                    memcpy( &cv->ary[i], &cv->ary[i+1], sizeof(Contact) );
                }
                printf( "\nContact deleted from Array of Contacts ...\n" );
                -- cv->size;
               
                free( deleteLastName ); /* free all tmp memory used here ... */
                free( deleteFirstName );
                return;
            }
        }
    }
   
    /* else ... */
    printf("\nContact NOT found ...\n\n");
    free(deleteLastName); /* free all tmp memory used here ... */
    free(deleteFirstName);
}

void showAllContacts( const Cvec* cv )
{
    int x = 0;
    printf( "\nContacts:\n" );
    for( x = 0; x < cv->size; ++x )
    {
        printf( "(%d)\n", x+1);
        printf( "Name: %s %s\n", cv->ary[x].firstName, cv->ary[x].lastName );
        printf( "Number: %s\n", cv->ary[x].phoneNumber );
    }
    printf( "size = %d, capacity = %d\n", cv->size, cv->cap );
}

To go to the next page, click on the '2' in the lower left corner below ...  where you see this ... Pages: [1] 2 
« Last Edit: October 29, 2011, 01:38:59 AM by David »