Developers Heaven Forum

Desktop Programming => C/C++ & Visual C++ => Topic started by: David on November 19, 2008, 12:19:49 AM

Title: New thread especially for students of C and C++
Post by: David on November 19, 2008, 12:19:49 AM
Update:   please see this next link:

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

FREE homework help NOW available via e-mail ... (or in person if in Toronto Ontario Canada region.)

You can contact me via:
http://sites.google.com/site/andeveryeyeshallseehim/home/he-comes
http://developers-heaven.net/forum/index.php/board,9.0.html



For now ... Please consider this site as just a kind of

Quote

scratch pad . . .


to collect demo programs and later organize them for an e-text aimed at University Students beginning C or C++   It may eventually parallel my HLA site, also at this forum.

Click here (for links to these e-texts):

BEGINNING COMPUTER PROGRAMMING (using C++ or C) (http://developers-heaven.net/forum/index.php/topic,127.0.html)

or here ...

BEGINNING COMPUTER PROGRAMMING (using HLA and Python) (http://developers-heaven.net/forum/index.php/topic,46.0.html)

This site may be for you, a natural continuation, via the HLA/Python introduction, to a quick advance in C/C++ programming. 

If you have worked through this HLA/Python site  ...

BEGINNING COMPUTER PROGRAMMING (using HLA and Python) (http://developers-heaven.net/forum/index.php/topic,46.0.html)

... and worked through many of the example programs there ...

then ... you should have a BIG JUMP START to coding in C or C++.


Shalom,

David

2008-11-18

For appropriate credits due ... please see the introduction (partly copied below) from this link ...

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

Acknowledgements:

This course and text would not be possible without the very gifted insights, teaching experiences, and programming skills of Randall Hyde,

http://en.wikipedia.org/wiki/Randall_Hyde

the Author, Creator, and so far, the Sustainer of HLA. I also would like to acknowledge the tremendous encouragement given by my youngest son Matthew, who's recent Engineering need to know a little C++, and then Assembly ... provided the incentive for a little more then that, from dad, (which led to HLA and now this attempt to benefit beginning computer students in general.)  Also, I would like to thank my students at Scarborough Christian High School, who besides being very promising young ladies and gentlemen, provided exemplary feedback as we went along into some not yet fully charted territory.



Table of Contents:

Chapter 01: The Computer prints a message
Chapter 02: The Computer gets some input
Chapter 03: The Computer adds some numbers
Chapter 04: The Computer repeats a procedure
Chapter 05: The Basic Elements of computer programming … and low level example(s)
Chapter 06: The Computer rolls the dice … a first simulation
Chapter 07: First Memory Flow Lab
Chapter 08: Memory Flow Lab2
Chapter 09: Memory Flow Lab3
Chapter 10: HLA (C/C++) Data Types
Chapter 11: The Computer does some Algebra with real numbers ... and Input Data Validation
Chapter 12: Pointers, Strings, your own Types, Records, Arrays, and dynamic things on the fly!
Chapter 13: The Computer files its Records
Chapter 14: The Computer reads its Files
Chapter 15: The Computer updates (edits, deletes, sorts, chops-duplicates-in) its Files
Chapter 16: The Computer … vast OS’s … and the garbage collects?  (Operating Systems ... what is DOS?)
Chapter 17: The Computer delivers daily Manna ... (gui Canadien ... eh?)
Chapter 18: OOP ... goes the computer?
Chapter 19: OOP 2
Chapter 20: Shaping OOP

Begin the e-text BEGINNING COMPUTER PROGRAMMING (with HLA) here ...

http://developers-heaven.net/forum/index.php?topic=2607.0
(Link updated 2013-05-24)
Title: Re: New thread especially for students of C and C++
Post by: David on November 19, 2008, 01:59:21 AM
NOTE: In the following programs, please be advised that you may still NEED to, or WANT to, revise/update them to handle the case of *** failure to allocate dynamic memory. ***

Here is some C code, and later, the same program in HLA.  You may thus easily see for yourself the many similarities in C and HIGH LEVEL HLA Assembly ;)

This program addresses a very common problem ... 'removing duplicates' in a data set.  One may not always care to pre-sort the data, so this 'remove' algorithm, (and probably using only a few recursive calls, to potentially vastly speed it up), zooms along your unsorted data and you soon end up with all your duplicates, each in neat groups, at the top of the data set.  

You may like to count and note the number of each unique duplicate and report that from this top-set 'side effect'.

Of course, if you want to use dynamic memory, and if you have MANY duplicates, you could re-size the memory allocated for this new and smaller data set of unique elements.  

One possible undesirable effect of this program, is that the first duplicate is replaced by the first 'top' element different to that search comparator. Thus ... some values at the top of the data set may appear near the front in the final data set of non-duplicates.


Here is the C version:


Code: [Select]
/*
    An efficient ...
    (and possibly slightly NEW METHOD - that uses recursive calls),
    of removing duplicates in an UNSORTED array of data ...
    
    by David W. Zavitz on 2008-11-16
    to contact, please see:
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

#include <stdio.h>

/*  
    Global variables used here for ease of testing purposes.
*/
int ARY[] = {0,0,1,5,8,8,9,1,1,2,3,4,3,2,2,2,7,9,9,3,3,3,4,4,4,6,2,2,1,1,0,0};
const int NUM = sizeof ARY / sizeof ARY[0];

/*
    'ARY' is the UNsorted array passed in, and ...
    'NUM' is the number of elements in that array.
    'unique' returns the index of the top non-duplicate element
    in the array after non duplicate elements moved down,
    and duplicates are now ... all at the 'top'.
*/
int unique(int array[], int n);
void swap(int array[], /* int start, */ int i, int *top);


int main()
{
    int i, topIndex;
    puts("\nArray BEFORE ...");
    for(i = 0; i < NUM; ++i)
            printf( "%d ", ARY[i] );
            
    topIndex = unique(ARY, NUM);
    printf
    (
        "\n\nArray AFTER 'unique(ARY, NUM)' called, "
        "returning 'topIndex' of %d ...\n",
        topIndex
    );
    for(i = 0; i <= topIndex; ++i)
            printf("%d ", ARY[i]);
    
    printf("\nThere were %d duplicates 'removed'.\n", NUM-1-topIndex);
    
    puts("\n\nArray AFTER ...");
    for(i = 0; i < NUM; ++i)
        printf("%d ", ARY[i]);
    printf
    (
        "\nshowing the NEW positions of all %d elements "
        "in the original array.", NUM
    );
    
    printf("\n\nPress 'Enter' key to continue ... ");      
    getchar();
    return 0;
}


void swap(int array[], /* int start, */ int i, int *top)
{
/*
    // for testing purposes ... (I also printed the 'start' index)
    printf
    (
        "\n\nInside swap, the start index is %d, "
        "the i index is %d, "
        "the top index is %d,\n"
        "the array is: ",
        start, i, *top
    );
    int j;
    for(j = 0; j < NUM; ++j)
        printf("%d ", ARY[j]);
*/
    if (i == *top) /* if at upper end of array, don't swap */
    {
        (*top)--;
        return;  
    }

    if(array[i] == array[*top]) /* recursive call since top element a duplicate */
    {
        (*top)--;
        swap(array, /* start, */ i, top);    
    }
    else /* swap values (at this 'i' and at this 'top') */
    {
        int temp = array[i];
        array[i] = array[*top];
        array[*top] = temp;
        (*top)--;
    }
}

int unique(int array[], int n)
{
    /* handle special cases: empty array, just one element */
    if (n <= 0) return -1;
    if (n == 1) return 0;
    
    int i;         /* i holds index of element being compared */
    int top = n-1; /* top holds running top index ...         */
    int start;     /* start holds running start index ...     */
    for(start=0; start<top; ++start)
    {
        for(i=start+1; i<=top; ++i) /* 'i' is index of element being compared */
        {
            if(array[start] == array[i]) /* if elements are different ... i++*/
                swap(array, /* start, */ i, &top);  
            /* else: get next i element to compare ... */
        }
    }
    return top;
}


Now see the similarities to HLA ...

and the extra control you have at your disposal to optimize the code, if desired. Note the 'similar feel' to C.

You may like to note the extra efficiency added by using  xchg(a, b); in the swaps ... And of course, you can use registers for all your counters ... There is no need, many times, to first copy a register to a variable, before you pass that value to a function.  Thus, you get a closer feel for all the steps involved ... with not that much more work than in C.

But to tell the truth, I first coded this in C ... and it took more than just a little while, to get it all debugged, and to feel that the logic and output matched, including making sure it correctly handled unusual values, like an array of just one element, or an array of all duplicates.

The translation process from C to HLA went fairly smoothly ... and was pretty straightforward.  But I was glad that I had on hand a seemly well tested/debugged C version ...

The algorithm did give me some difficulty in getting its seemly simple idea out of my head and into a C version. I gained confidence with much varied test data.  This helped confirm that the code would handle, as expected, all possible data sets.

Another thing I should mention, is that I used a GLOBAL array in HLA ... so that I didn't have to pass it to the procedures.  In C ... I can just use the pointer as the array inside my function... for example p[0] ...comparable to ary[0]  ... I may later, (now done - see the last program below, in this section), submit an HLA version that demonstrates an HLA pointer.


Code: [Select]
program removeDuplicates;
/*
    An efficient ...
    (and possibly slightly NEW METHOD - that uses recursive calls),
    of removing duplicates in an UNSORTED array of data ...
    
    by David W. Zavitz on 2008-11-17
    to contact, please see:
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

#include( "stdlib.hhf" )

?@nodisplay := true;
/*  
    Global variables used here for ease of testing purposes.

    'ARY' is the UNsorted array passed in, and ...
    'NUM' is the number of elements in that array.
    'unique' returns the number of non-duplicate elements
    in the array after non duplicate elements moved down,
    and duplicates are now ... all at the 'top'.
*/

static
ARY: int32[]:= [0,0,1,1,2,3,3,4,5,6,4,7,8,9,8,8,7,8,8,8,9,3,3,3,2,0,1,1,1,0,0,7];

// Array AFTER ... 0 7 1 2 3 9 8 4 5 6 4 8 8 8 8 8 9 3 3 3 3 2 1 1 1 1 7 7 0 0 0 0
// with duplicates all moved to the top end

// Array AFTER 'unique(ARY, NUM)' called, the returned 'topIndex' is 9
// 0 7 1 2 3 9 8 4 5 6
// There were 22 duplicates 'removed'.

const
NUM: int32:= @size(ARY) div @size(ARY[0]);

/* show 'thisNum' of int32's in array 'thisAry' */
#macro showAry( thisAry, thisNum );
    push( ecx );
    for( mov( 0, ecx ); ecx < thisNum; inc( ecx ) ) do
        stdout.puti32( thisAry[ecx*4] );
    endfor;
    pop( ecx );
#endmacro

/* uses Globals ARY, and NUM ... returns new 'top' value by reference */
procedure swap( start:int32;  i:int32; var top:int32);
begin swap;
    push( eax ); push( ebx ); push( ecx ); push( edx );

    /* dereference top ... get value into ebx */
    mov( top, ebx );
    mov( [ebx], ebx );

    /* for testing purposes ...I printed these:  'start' index, 'i' index, 'top' index */
    stdout.put
    (
        nl nl "Inside swap, the start index is ", start,
        ", the i index is ", i,
        ", the top index is ", (type int32 ebx), ",", nl
        "the array is: "
    );
    showAry( ARY, NUM );

    if (i == ebx) then /* if at upper end of array, don't swap */
        dec(ebx); /* top--; */  
     mov( top, edx );
mov( ebx, [edx] ); /* update top ... */
    else
mov( i, ecx );
mov( ARY[ecx*4], edx );
if( edx == ARY[ebx*4]) then          /* recursive call since top element is a duplicate */
       dec(ebx); /* top--; */
       mov( top, edx );
       mov( ebx, [edx] ); /* update top ... */
       swap( start, i, top);    
else        /* swap values (at this 'i' and at this 'top') */
    mov( ARY[ecx*4], eax );
       xchg( eax, ARY[ebx*4 ]);
       mov( eax, ARY[ecx*4]);
       dec(ebx); /* top--; */
     mov( top, edx );
mov( ebx, [edx] );        /* update top ... */
endif;
    endif;

    pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end swap;

/* uses Global array 'ARY' ... number of elements 'passed in' via 'n' */
/* eax returns the number of the top index in the 'new' array  */
procedure unique(n:int32); @returns("eax");
var
    top: int32;
    //i: int32; /* ecx=i holds index of element being compared */
    //start: int32;
begin unique;
    push( ebx ); push( ecx );

    /* handle special cases first */
    if (n <= 0) then /* (1.) empty array */
     mov( -1, eax );
    elseif (n == 1) then        /* (2.) just one element */
     mov( 0, eax );
    else
   mov( n, eax ); /* using eax as a 'scratch-pad' register */
   dec( eax ); /* n = n-1; */
   mov( eax, top ); /* top holds running top index ... initial top = n-1; */
   
   /* eax holds running 'start' index ...     */
   for( mov( 0, eax); eax<top; inc( eax ) ) do
    mov( eax, ecx );
    /* i begins with value i=start+1, thus the following begin with ... inc(eax) */
       for( inc(ecx); ecx<=top; inc(ecx) ) do /* ecx = 'i' is index of element being compared */
           mov( ARY[eax*4], ebx ); /* ebx holds 'start' value */
           if( ebx == ARY[ecx*4] ) then /* if elements are different ... i++*/
            //mov( ecx, i );
            //mov( eax, start);
               swap( eax, ecx, top ); //swap(start, i, top)
           endif;
           /* else: get next i=ecx element to compare ... */
       endfor;
   endfor;
   mov( top, eax );
    endif;
    pop( ecx ); pop( ebx );
end unique;

static
    topIndex: int32;
    dupRemoved: int32;

begin removeDuplicates;

    stdout.puts( nl "Array BEFORE ...");
    showAry( ARY, NUM );
    
    //unique(NUM); // returns eax // uses Global array ARY //    
    mov( unique(NUM), topIndex );
    stdout.put
    (
        nl nl "Array AFTER 'unique(ARY, NUM)' called, "
        "the returned 'topIndex' is ", topIndex, nl
    );
    inc( topIndex ); // set to equal instead, the number of elements
    showAry( ARY, topIndex );
    
    mov( NUM, eax );
    sub( topIndex, eax ); // 'topIndex' now holds the number of non-duplicate elements
    mov( eax, dupRemoved ); //dupRemoved = NUM - number_of_non-duplicate_elements
    stdout.put(nl "There were ", dupRemoved, " duplicates 'removed'." nl);
    
    stdout.puts(nl nl "Array AFTER ...");
showAry( ARY, NUM );
    stdout.put
    (
        nl "showing the NEW positions of all ", NUM,
        " elements in the original array."
    );    

end removeDuplicates;


Now here is that HLA version that passes the array by using a pointer ...


Code: [Select]
program removeDuplicates2; // pointer version
/*
    An efficient ...
    (and possibly slightly NEW METHOD - that uses recursive calls),
    of removing duplicates in an UNSORTED array of data ...
    
    by David W. Zavitz on 2009-01-03
    to contact, please see:
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

#include( "stdlib.hhf" )

?@nodisplay := true;

type
pAry: pointer to int32;

static
ARY: int32[]:= [0,0,1,1,2,3,3,4,5,6,4,7,8,9,8,8,7,8,8,8,9,3,3,3,2,0,1,1,1,0,0,7];

// Array AFTER ... 0 7 1 2 3 9 8 4 5 6 4 8 8 8 8 8 9 3 3 3 3 2 1 1 1 1 7 7 0 0 0 0
// with duplicates all moved to the top end

// Array AFTER 'unique(ARY, NUM)' called, the returned 'topIndex' is 9
// 0 7 1 2 3 9 8 4 5 6
// There were 22 duplicates 'removed'.

const
NUM: int32:= @size(ARY) div @size(ARY[0]);

/* show 'thisNum' of int32's in array 'thisAry' */
#macro showAry( pToArray, thisNum );
    push( ebx ); push( ecx );
    mov( pToArray, ebx ); /* get base address of array into ebx */
    for( mov( 0, ecx ); ecx < thisNum; inc( ecx ) ) do
        stdout.puti32( [ebx+ecx*4] );
    endfor;
    pop( ecx ); pop( ebx );
#endmacro

/* uses Globals ARY, and NUM ... returns new 'top' value by reference */
procedure swap( a:pAry; start:int32;  i:int32; var top:int32);
begin swap;
    push( eax ); push( ebx ); push( ecx ); push( edx ); push( edi );

    /* dereference top ... get value into ebx */
    mov( top, ebx );
    mov( [ebx], ebx );

    /* for testing purposes ...I printed these:  'start' index, 'i' index, 'top' index */
    stdout.put
    (
        nl nl "Inside swap, the start index is ", start,
        ", the i index is ", i,
        ", the top index is ", (type int32 ebx), ",", nl
        "the array is: "
    );
    showAry( a, NUM );

    if (i == ebx) then /* if at upper end of array, don't swap */
        dec(ebx); /* top--; */  
     mov( top, edx );
mov( ebx, [edx] ); /* update top ... */
    else
mov( i, ecx );
mov( a, edi ); /* get address in pointer to array into edi */
mov( [edi+ecx*4], edx );
if( edx == [edi+ebx*4]) then          /* recursive call since top element is a duplicate */
       dec(ebx); /* top--; */
       mov( top, edx );
       mov( ebx, [edx] ); /* update top ... */
       swap( a, start, i, top);    
else        /* swap values (at this 'i' and at this 'top') */
    mov( [edi+ecx*4], eax );
       xchg( eax, [edi+ebx*4 ]);
       mov( eax, [edi+ecx*4]);
       dec(ebx); /* top--; */
     mov( top, edx );
mov( ebx, [edx] );        /* update top ... */
endif;
    endif;

    pop( edi ); pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end swap;

/* number of elements 'passed in' via 'n' */
/* eax returns the number of the top index in the 'new' array  */
procedure unique(a:pAry; n:int32); @returns("eax");
var
    top: int32;
    //i: int32; /* ecx=i holds index of element being compared */
    //start: int32;
begin unique;
    push( ebx ); push( ecx ); push( edx );
    
    mov( a, edx ); /* get base address of array into edx */

    /* handle special cases first */
    if (n <= 0) then /* (1.) empty array */
     mov( -1, eax );
    elseif (n == 1) then        /* (2.) just one element */
     mov( 0, eax );
    else
   mov( n, eax ); /* using eax as a 'scratch-pad' register */
   dec( eax ); /* n = n-1; */
   mov( eax, top ); /* top holds running top index ... initial top = n-1; */
   
   /* eax holds running 'start' index ...     */
   for( mov( 0, eax); eax<top; inc( eax ) ) do
    mov( eax, ecx );
    /* i begins with value i=start+1, thus the following begin with ... inc(eax) */
       for( inc(ecx); ecx<=top; inc(ecx) ) do /* ecx = 'i' is index of element being compared */
           mov( [edx+eax*4], ebx ); /* ebx holds 'start' value */
           if( ebx == [edx+ecx*4] ) then /* if elements are different ... i++*/
            //mov( ecx, i );
            //mov( eax, start);
               swap( a, eax, ecx, top ); //swap(arrayAddress, start, i, top)
           endif;
           /* else: get next i=ecx element to compare ... */
       endfor;
   endfor;
   mov( top, eax );
    endif;
    pop( edx ); pop( ecx ); pop( ebx );
end unique;

static
    topIndex: int32;
    dupRemoved: int32;

begin removeDuplicates2;

    stdout.puts( nl "Array BEFORE ...");
    showAry( &ARY, NUM );
    
    unique(&ARY, NUM); // returns eax    
    mov( eax, topIndex );
    stdout.put
    (
        nl nl "Array AFTER 'unique(&ARY, NUM)' called, "
        "the returned 'topIndex' is ", topIndex, nl
    );
    inc( topIndex ); // set to equal instead, the number of elements
    showAry( &ARY, topIndex );
    
    mov( NUM, eax );
    sub( topIndex, eax ); // 'topIndex' now holds the number of non-duplicate elements
    mov( eax, dupRemoved ); //dupRemoved = NUM - number_of_non-duplicate_elements
    stdout.put(nl "There were ", dupRemoved, " duplicates 'removed'." nl);
    
    stdout.puts(nl nl "Array AFTER ...");
showAry( &ARY, NUM );
    stdout.put
    (
        nl "showing the NEW positions of all ", NUM,
        " elements in the original array."
    );    

end removeDuplicates2;

Title: Re: New thread especially for students of C and C++
Post by: David on November 19, 2008, 03:15:34 AM
The previous program(s) used pointer or reference variables.

In HLA, 'val' indicates that the address was taken in the function header line.  This address must be de-referenced inside the procedure to get the value pointed at.  In C we also need to de-reference the address passed in via the pointer variable.

Thus ... it may be timely to back up some ... and to review several simple pointer basics?

The tricky part about C is often getting and using 'pointers' ...  :-\

You may have encountered already many conflicting points of view to point you on your way.

But in C ...a  pointer is simply a variable that holds the address of another variable (or fixed object).

So when first passing a pointer a value, you give it some 'valid address' of an other variable (or object) in memory. This is a way to reference a variable ... You make reference to it ... NOT NOW by it's name ... but by its pointer, i.e. its address, (held in the pointer variable.) So the pointer ... refers us back, (points us back), to the address of the original variable (or object).

To get at the value in the original variable, using a pointer, you must de-reference the pointer like this:

... if p is the pointer then *p is the value at that address pointed to ...

char tmp = 'A';
char *p = &tmp; /* char *p; p = &tmp; */
printf("\n %c is the same as %c \n", tmp, *p );


Now the often confusing part is that the pointer variable itself is a variable and has its own address in memory. So you can have a pointer to that address also ... thus ...a pointer to a pointer variable ... (Or 2 levels of indirection.)  8)



Ok ... let's take a simple example:


Code: [Select]
/* pointers 102 in C */

#include <stdio.h>
#include <ctype.h>

/* name is declared as a variable to hold an address of a char */
void show( char * name )
{
    /* get a copy of value in name into p  (name is a local pointer variable) */
    
    char *p = name;  /* p is declared as a variable to hold an address of a char */
    
    /* will NOT accept const char strings like myName2 */        /* <---<<< */
    while( *p != 0 ) { *p = toupper(*p); p++; }
    
    printf( "%s", name );
}

/*
    In this example of a C string,
    this also works ...
*/

/* name is declared as a variable to hold an address of a char */
void show2( char name[] )
{
    /* recall: "%s" takes an address to a NULL terminated array of char */
    printf( "%s", name );
}

int main()
{
    char myName1[] = "David W.";
    
    /* a const char C 'string' */
    char * myName2 =  "Zavitz";  /* myName2 is declared a pointer */
    
    /* Take the address at memory labeled myName1 with char offset 0 */
    //char * pMyName = &myName1[0];
    
    /*  or could use the below ... since ...*/
    
    char * pMyName = myName1;   /* recall myName1 is already an address */
    
    show( myName1 );
    printf( "... and again is " );
    show( pMyName );
    
    printf( "\n... and again is " );
    show2( myName1 );
    printf( "... and again is " );
    show2( pMyName );
    
    
    printf("\n\nOr ...\n\n");
    
    
    show( myName1 );
    printf( "... and again is " );
    /* show( myName2 ); */                                      /* <---<<< */
    
    printf( "\n... and again is " );
    show2( myName1 );
    printf( "... and again is " );
    show2( myName2 );  
    
    getchar();
    return 0;
}
Title: Re: New thread especially for students of C and C++
Post by: David on November 19, 2008, 03:51:27 AM
Changing the pace some ... and with a little play on words in an attempt to add some light hearted humour to this sobering subject of dates ... and perhaps, just how late the date is ...   :-X

re. how late the date is, please see:

http://sites.google.com/site/andeveryeyeshallseehim/

The following features the basic record keeping structure, a struct, common to both C and C++. Struct's have a little more relaxed useage in C++.  You could instead use a class with public data in C++.  In HLA we have type record and type class, somewhat comparable to C++.


Here is an HLA example of the header for Vector class ...


Code: [Select]
type
Vector:
class
var
x: real32;
y: real32;
procedure create;
procedure get_x; @returns( "st0" );
procedure get_y; @returns( "st0" );
method set_x( r:real32 );
method set_y( r:real32 );
procedure takeIn;
procedure display;
procedure findLength; @returns( "st0" );
procedure findAngle; @returns( "st0" );
endclass;


Or just a record ...


Code: [Select]
type
Vector:
record
x: real32;
y: real32;
endrecord;


Compare this with a fancied up record, a struct, in C


Code: [Select]
typedef struct Date 
{
   int  mm,
        dd,
        yy;
}Date;


The following C 'date' program uses lots of pointers and introduces the C/C++ notation for a pointer pointing to a record element ...

If d is a pointer to date, then d->yy is the year field content value.

You might like to scan this 'tongue in cheek' student guide to dating ... using C   :-*

Good 'dating' does require a few advanced techniques ...

but with a little patience and perseverance ...

and careful attention to details,

you will progress and have good success


Code: [Select]
/*
    Dates ... with C
   
    Some (a little and/or not so) novel 'dating' pointers ... for C students.
   
    To strengthen your foundations in programming,
    or to contact the author, please look here ...
   
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

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

typedef struct Date /* the main attraction */
{
   int  mm,
        dd,
        yy;
}Date;

/* 11 very forward declarations ...*/

Date * getNewDate();
Date * newCopy( Date *d );
void show( Date *d );
void advance( Date *d );
int isValid( Date *d );
int isLeapYear( int year );
int isLess( Date *d1, Date *d2 );
int isEqual( Date *d1, Date *d2 );
int exceedsDaysInMonth( Date *d );
int difference( Date d1, Date d2 );
int more();

/* Note well the progression above, but the main object is ... 'to get a Date'. */


int main() /* to test out all the 'dating tips' introduced above */
{
    Date *pd;
    Date *pd2;
    int i, n;
    do
    {
        pd = getNewDate();
        printf("You entered the Date : "); show( pd );
        advance( pd );
        printf("\nTomorrow's Date is   : "); show( pd );
       
        for(i=1; i<=6; ++i) advance( pd );
        printf("\nOne weeks Date is    : "); show( pd );
       
        for(i=1; i<=21; ++i) advance( pd );
        printf("\nFour weeks Date is   : "); show( pd );
       
        for(i=1; i<=365-28; ++i) advance( pd );
        printf("\n365 days Date is     : "); show( pd );
       
        printf("\n\nEnter a 'base' Date to count from ...");
        free( pd );
       
        pd = getNewDate();
        pd2 = newCopy( pd ); /*  make a 'new' backup copy ... */
        printf("You chose the Date   : "); show( pd );
       
        printf("\nEnter the number of days to advance : ");
        scanf("%d", &n);
        while(getchar() != '\n'); /* 'flush' stdin ... */
        for(i=1; i<=n; ++i) advance( pd );
        printf("%d days into the future is : ", n); show( pd );
       
        if( isLess( pd, pd2 ) ) printf("\nisLess( pd, pd2 ) is true.\n");
        else printf("\nisLess( pd, pd2 ) is false.\n");
       
        printf("\n\nDouble ckecking days between ...\n");
        show( pd ); printf( " and " );  show( pd2 );
        n =  difference( *pd, *pd2 );
        printf("\nDays between equals  : %d", n);
        printf("\nAs 'inclusive' days  : %d", 1+n);
     
        printf("\n\nDouble ckecking days between ...\n");
        show( pd2 ); printf( " and " );  show( pd );
        n =  difference( *pd2, *pd );
        printf("\nDays between equals  : %d", n);
        printf("\nAs 'inclusive' days  : %d", 1+n);
       
        free( pd2 );
        free( pd );
           
    }while( more() );
       
    return 0;
}


/* 11 not so secret Date function definition tips ...*/


int isLeapYear( int year )
{
    if(year%4 != 0) return 0;  /* not a leap year */
    if(year%100 == 0 && year%400 != 0) return 0; /* not a leap year */
    return 1;
}

int exceedsDaysInMonth( Date *d )
{
    int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
   
    if( !isLeapYear( d->yy ) )
    {
        if ( d->mm > 12 || d->dd > days[d->mm] ) return 1;   
       
        return 0;
    }
    else /* is leap year ... so need to handle case of month == Feb */
    {
        if ( d->mm == 2 && d->dd == 29 ) /* <-- the 'tricky' condition to get right */
            return 0;  /* NOTE: returns FALSE here since Date IS VALID */
        if ( d->mm > 12 || d->dd > days[d->mm] )
            return 1;
           
        return 0;
    }
}

void advance( Date *d )
{
    d->dd++;                       /* first increment the day */
    if( exceedsDaysInMonth( d ) )  /* if it crosses over to the next month */
    {
        d->dd = 1;                  /* the day becomes the first of next month */
        d->mm++;                    /* increment month in that case */
        if(d->mm > 12)              /* if it crosses over to the next year */
        {
            d->mm = 1;              /* the month becomes the first month */
            d->yy++;                /* of the next year */
        }
    }
}


int isValid( Date *d )
{
    /* if month not in 1..12 invalid ... */
    if( d->mm< 1 || d->mm > 12 ) return 0;
    if ( exceedsDaysInMonth( d ) ) return 0;
/*
    The following bit of text was copied from an HLA program by Randy Hyde
   
    The following is questionable.
    Who knows what ranges we should check? 1600 is probably a good starting year
    since this was shortly after the advent of the Gregorian calendar.  The upper
    end should be reasonably small so as to catch ridiculous Dates, but large
    enough so that we don't suffer from the "Y2K Syndrome".  9999 is probably
    way too big (2200 would probably be a good number for now), but who knows
    what to use here?   
*/
    if (d->yy <1600 || d->yy>9999) return 0;
    return 1;
}

int isLess( Date *d1, Date *d2 )
{
    if( d1->yy < d2->yy ) return 1;
    if( d1->yy == d2->yy && d1->mm < d2->mm ) return 1;
   
    if(
        d1->yy == d2->yy
        && d1->mm == d2->mm
        && d1->dd < d2->dd
       )   
        return 1;
    /* if reach here ... */
    return 0;   
}

int isEqual( Date *d1, Date *d2 )
{
    if (
            d1->yy == d2->yy
            && d1->mm == d2->mm
            && d1->dd == d2->dd
        )
        return 1;
    /* if reach here ... */
    return 0;
}

Date * newCopy( Date *d )
{
    Date * tmp = (Date*) malloc( sizeof(Date) );
    tmp->dd = d->dd;
    tmp->mm = d->mm;
    tmp->yy = d->yy;
    return tmp;
}
   
int difference( Date d1, Date d2 )
{
    int diffDays = 0;
    if( isLess( &d1, &d2 ) )
    {
        while( !isEqual(&d1, &d2) )
        {
            diffDays++;
            advance( &d1 );   
        }
    }
    else
    {
        while( !isEqual(&d2, &d1) )
        {
            diffDays++;
            advance( &d2 );   
        }
    }
    return diffDays;
}

Date * getNewDate()
{   
    Date * d = (Date*) malloc( sizeof(Date) );
    printf("\nEnter a valid Date in the format yyyy/mm/dd : ");
    for(;;) /* loop forever ... until break */
    {
        scanf("%d",&d->yy);
        getchar(); /* to 'eat' the '/' char's ... */
        scanf("%d",&d->mm);
        getchar();
        scanf("%d",&d->dd);
        while(getchar() != '\n'); /* 'flush' stdin ... */

        if( isValid(d) ) break;

        /* if program reaches here ... */
        printf
        (
            "\nInvalid Date. "
            "Enter Date in range 1600/1/1..9999/12/31 : "
        );
    }
    return d;
}

void show( Date *d )
{   
    char * nameOfMonth[]=
    {
        "***", "January", "February", "March",  "April", "May", "June",
        "July", "August", "September", "October", "November", "December"
    };
    printf("%s %d, %d", nameOfMonth[d->mm], d->dd, d->yy);
}

int more()
{
    printf("\n\nMore (y/n) ? ");
    char reply;
    scanf("%c", &reply);
    int dummy = (int)reply;
    while(dummy!='\n') dummy = getchar();
    return !(reply=='n' || reply =='N');
}


Below is a C++ Date class program, (much like the C date struct program listed above) ...


Code: [Select]
/*
    Finds the days inclusive between two valid dates
    Demo of ...  Validation for input Dates
*/

#include<iostream>
#include<string>

using namespace std;

class Date
{
public:
    Date() { month=1; day=1; year=1900; }   
    Date( int m, int d, int y ) { month = m; day = d; year = y; }           
   
    int GetMonth() { return month; }
    int GetDay() { return day; }
    int GetYear() { return year; }
    int difference( Date &date2 );

    void display_date();   
 
    bool is_equal( Date& );
    bool is_less_than( Date& );
    bool isValid();
   
private:
    int month;
    int day;
    int year;
   
    bool isLeapYear();
    void advance();
    string month2string();
    bool exceedsDaysInMonth();
};


int main()
{
    int m, d, y, days_apart;
    char slash; 
    cout << "This program gives the number of days between two dates.\n\n"
         << "Press Ctrl C to exit the loop ...\n\n";
         
    for(;;)
    {
        cin.sync();
        cout<<"Please enter 1st date in the form yyyy/mm/dd : ";
        cin>>y>>slash>>m>>slash>>d;
        if ( !cin.good() )
        {
            cin.clear();
            cout << "\nERROR! Bad date entered ... \n\n";
            continue;
        }
        Date First_Date(m, d, y);
        if ( First_Date.isValid() )
        { 
            cout<<"1st Date is  : ";
            First_Date.display_date();
        }
        else
        {
            cout << "\nERROR! Bad date entered ... \n\n";
            continue;
        }
        Date Second_Date;
        for(;;)
        {       
            cin.sync();
            cout<<"\nPlease enter 2nd date in the form yyyy/mm/dd : ";
            cin>>y>>slash>>m>>slash>>d;
            if ( !cin.good() )
            {
                cin.clear();
                cout << "\nERROR! Bad date entered ... \n\n";
                continue;
            }
            Date temp(m, d, y);
            if ( temp.isValid() )
            { 
                Second_Date = temp;
                cout<<"2nd Date is  : ";
                Second_Date.display_date();
                break; // out of inner loop
            }
            else
            {
                cout << "\nERROR! Bad date entered ... \n\n";
                continue;
            }
        }
       
        cout << endl << endl;
        if( First_Date.is_equal(Second_Date) )
        { 
            cout<<"The first date you entered is the same as the second date."<<endl;
            days_apart = First_Date.difference(Second_Date);
        }
        else if( First_Date.is_less_than(Second_Date) )
        {
            cout<<"The first date you entered is earlier than the second date."<<endl;
            days_apart = First_Date.difference(Second_Date); 
        }
       
        else // the date can only be equal or less than ...or ELSE it is greater
        {   
            cout<<"The first date you entered is later than the second date"<<endl;
            days_apart = Second_Date.difference(First_Date);
        }
       
        cout<<"The two dates are "<< days_apart<<" days apart.\n"
            <<".. or have " << days_apart+1 << " inclusive days between them.\n\n"
            << flush;
    }
}


int Date::difference( Date &date2 )
{
    int diff_days = 0;
    while ( !this->is_equal(date2) )
    {
        diff_days++;
        advance();   
    }
    return diff_days;
}

bool Date::exceedsDaysInMonth()
{
    int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
   
    if( !isLeapYear() )
    {
        if ( month > 12 || day > days[month]) return true;   
       
        return false;
    }
    else // is leap year ... so need to handle case of month == Feb
    {
        if ( month == 2 && day == 29 ) //<-- the 'tricky' condition to get right
            return false; 
        if ( month > 12 || day > days[month] )
            return true;
           
        return false;
    }
}

// convert numeric month into string
string Date::month2string() 
{
    char *months[] =
    {"***", "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
    if ( !exceedsDaysInMonth() )
    {
        if ( month >= 1 && month <= 12 )
            return months[month];
        else
            return "Unknown month";
    }
    else
        return "Unknown month";
}

bool Date::isLeapYear()
{
    if (year%4 != 0) return false;  // not a leap year
    if (year%100 == 0 && year%400 != 0) return false; // not a leap year
    return true;
}

void Date::advance()
{
    day++;                          //first increment the day
    if( exceedsDaysInMonth() )      //if it crosses over to the next month
    {
        day = 1;                    //the day becomes the first of next month
        month++;                    //increment month in that case
        if(month > 12)              //if it crosses over to the next year
        {
            month = 1;              //the month becomes the first month
            year++;                 //of the next year
        }
    }
}

void Date::display_date()
{   
    string name_of_month[]=
    {
        "***", "January", "February", "March",  "April", "May", "June",
        "July", "August", "September", "October", "November", "December"
    };
    cout <<  name_of_month[month]  <<  " "  <<  day  <<  ", "  << year;
}
   
bool Date::is_equal( Date& Second_Date )
{
    if (
            month==Second_Date.month
            && day==Second_Date.day
            && year==Second_Date.year
        )
        return true;
    // if reach here ...
    return false;
}
   
bool Date::is_less_than( Date& Second_Date )
{   
    if( year < Second_Date.year) return true;
    if( year <= Second_Date.year && month < Second_Date.month ) return true;
    if(
        year <= Second_Date.year
        && month <= Second_Date.month 
        && day < Second_Date.day
       )   
        return true;
    // if reach here ...
    return false;   
}

bool Date::isValid()
{
    // if month not in 1..12 invalid ...
    if( month< 1 || month > 12 ) return false;
    if ( exceedsDaysInMonth() ) return false;
/*
    The following is questionable.
    Who knows what ranges we should check? 1600 is probably a good starting year
    since this was shortly after the advent of the Gregorian calendar.  The upper
    end should be reasonably small so as to catch ridiculous dates, but large
    enough so that we don't suffer from the "Y2K Syndrome".  9999 is probably
    way too big (2200 would probably be a good number for now), but who knows
    what to use here?   
*/
    if (year <1600 || year>9999) return false;
    return true;
}


/*
Quick question, is the year 2000 a leap year? A suprising number of people give
the wrong answer. The correct answer is "yes", that is 2000 is a leap year.
Why do so many folks give the wrong answer to this simple question? In general,
a year is a leap year if it is evenly divisible by 4.
Most people also remember the exception which is a year is not a leap year if
it ends in "00" (ie., the year is even divisable by 100). So years like 1972,
1980, and 1996 are leap years, but 1800 and 1900 are not.

So why then is a 2000 a leap year? What many folks do not realize is that the
100 year exception itself has an exception. Years that are evenly divisable by
400 are leap years. So 2000 as well as 2400 will be leap years.

Programmers who write date conversion code and don't know about the "exception
to the exception" will end up creating software which will display incorrect
dates starting Feb. 29th, 2000. The incorrect conversions software will instead
give Feb. 29th as March 1. Even worse, all subsequent dates will likely be off
by one day also.

Although the Leap Year 2000 bug is not technically the same as the Y2K bug, it
is many times lumped together Y2K bug because it occurs so close to Jan. 1, 2000.

Over the years, operating systems and development tools have shipped with the
"Leap Year 2000" bug in them. This means that applications that use these broken
operating systems or tools will also have the bug. However, over the last 5
years with the all of the talk of the Y2K bug, most of the problems have been
fixed.

*/


A simple approach to 'Dates' in C++ is given below ... that would not fit in this page.
Title: Re: New thread especially for students of C and C++
Post by: admin on November 19, 2008, 10:32:34 AM
Thanks David for all your efforts.
Title: Re: New thread especially for students of C and C++
Post by: David on November 29, 2008, 01:09:18 PM
Here is a simple approach to dates ... coded in C++ (that wouldn't fit in the 'date' section above) ...

Code: [Select]
// Find number of days from 1900-01-01 ...

#include <iostream>
#include <iomanip>

using namespace std;

const int daysInYear[ ] = {0,31,59,90,120,151,181,212,243,273,304,334,365};

bool more()
{
    cout << "\nMore (y/n) ? " << flush;
    char reply;
    cin.clear();
    cin.sync();
    reply=cin.get();
    cin.sync();
    return !(reply=='n' || reply=='N');
}

long toHrs( long days )
{
    return days*24;    
}

long toMins( long days )
{
    return days*24*60;
}

long long toSecs( long days )
{
    return ((long long)days)*24*3600;
}


int main ()
{
    long yr, mo, da, years, days;
    char c;
    
    cout << "This program reads a date and finds\n"
         << "the number of inclusive days since 1900-01-01.\n"
         << "Results valid in date range 1900-01-01 to 2099-12-31.\n";
    do
    {
        cout << "\nEnter a valid 'test' date yyyy-mm-dd : ";
        cin >> yr >> c >> mo >> c >> da;
    
        // first find the whole years that have passed ...
        years = yr-1900;
        
        // convert this to days ...adding one day for each leap year that has passed
        // note 1900 was NOT a yeap year but 2000 WAS a leap year ... so ...
        days = years*365 + years/4;
        
        // now add on the days for the whole months that have passed ...
        days = days + daysInYear[ mo-1 ];
        
        // now add on the days in this month that have passed ...
        days = days + da;
        
        // if in a leap year & if before March, correct for the already added day
        if( yr%4==0 && mo<3 && yr!=1900 ) --days;
        
        cout << "The number of inclusive days from 1900-01-01 to "
             << yr << "-" << setfill('0') << setw(2) << mo << "-"
             << setw(2) << da << " is " << days << ".\n"
             << "Or at midnight, " << toHrs(days) << " hours or "
             << toMins(days) << " minutes or " << toSecs(days) << " seconds.\n";
    
    }while( more() );
}


And here is a common student date problem ... printout a calander for any month in a year from ...? to ...?

Coded here in C ...

Code: [Select]
/*
    Print a calendar from 1600 to 9999
    
    For calibration ref. see ...
    http://www.nr.com/julian.html
*/

#include <stdio.h>

#define HEADER "This program prompts the user to enter a month and a year\n" \
                "and generates a calendar for that period. For example ...\n" \
                "Enter month : 5\n" \
                "Enter year  : 2018\n\n" \
                "Su  M Tu  W Th  F Sa\n" \
                "       1  2  3  4  5\n" \
                " 6  7  8  9 10 11 12\n" \
                "13 14 15 16 17 18 19\n" \
                "20 21 22 23 24 25 26\n" \
                "27 28 29 30 31\n"

#define HEADER_CAL "Su  M Tu  W Th  F Sa"

/*
    This function asks the user to ...
    Enter month : 5
    Enter year  : 2018
    
    And returns, by reference, month and year entered
    Note: Allows only valid input ... month range 1..12, year range 1900..9999
*/
void enterMonthYear(int *month, int *year)
{
    int numGood;
    for( ;; ) /* loop forever ... until break */
    {
        printf("Enter month : ");
        if( (numGood=scanf("%d", month))!=1 || *month<1 || *month>12 )
        {
            while( getchar() != '\n' ) ; /* flush stdin */
            printf("Valid months are 1..12  ");
        }
        else
        {
            while( getchar() != '\n' ) ; /* flush stdin */
            break;
        }
    }

    for( ;; )
    {
        printf("Enter year  : ");
        if( (numGood=scanf("%d", year))!=1 || *year<1600 || *year>9999 )
        {
            while( getchar() != '\n' ) ; /* flush stdin */
            printf("Valid years are 1600..9999  ");
        }
        else
        {
            while( getchar() != '\n' ) ; /* flush stdin */
            break;
        }
    }
}

/* returns 1 if leap year, else returns 0 */
int isLeapYear( int year )
{
    if(year%4 != 0) return 0;  /* not a leap year */
    if(year%100 == 0 && year%400 != 0) return 0; /* not a leap year */
    return 1;
}

/* returns the number of days for a month in any year 1900..9999*/
int daysInMonth(int month, int year)
{
    int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    if( month==2 && isLeapYear(year) ) return days[month] +1;
    else return days[month];
}

/* returns day number for any day and month in any year 1600..9999 */
int getDayNum(int month, int day, int year)
{
    int dayNum = 0;
    int monthNum;
    for( monthNum=1; monthNum<month; ++monthNum)
        dayNum += daysInMonth(monthNum, year);
    dayNum += day;
    return dayNum;
}

/* returns the number of days from 1/1/1600 to the end of the previous 'year' */
long yearsToDays(int year)
{
    int numDays =0;
    int yearNum;
    for(yearNum=1600; yearNum<year; ++yearNum)
        numDays += 365 + isLeapYear(yearNum);
    return numDays;
}

/*
    Outputs the calendar to the screen ...
    'startDay' says which day of the week is the first of the month
    (0 is Sunday, 1 is Monday, etc...)
    'numDays' is how many days in the month
*/
void printCalendar(int startDay, int numDays)
{
    int i;
    puts(HEADER_CAL);
    
    for(i=0; i<startDay; ++i)
        printf("   ");

    for(i=1; i<= numDays; ++ i)
    {
        printf("%2d ", i );
        if( i< numDays && (i+startDay)%7 == 0 ) puts("");
    }
}

/* defaults to 'yes' */
int more()
{
    char c, reply;
    printf("\n\nMore (y/n) ? ");
    reply=c=getchar();
    while( c != '\n' ) c = getchar(); /* flush stdin */
    return !(reply=='n' || reply=='N');
}


int main()
{
    int month, year, startDay;
    puts( HEADER );
    
    do
    {
        enterMonthYear( &month, &year );
/*
        printf("\n%d %s a leap year.\n",
                year, isLeapYear(year) ? "is" : "is not");
        
        printf("\nDay 1 of the %d month of year %d is day number %d\n",
                month, year, getDayNum(month, 1, year) );
                
        printf("\nNumber of days from 1600/01/01 to the end of the year %d is %d\n",
                year-1, yearsToDays(year) );
*/
        /* 2305448 1600/01/01 was a Saturday; 2415021 1900/01/01 was a Monday */
        startDay = (5 + yearsToDays(year) + getDayNum(month, 1, year)) % 7;
/*
        printf("\nStart day for month %d and year %d is %d\n",
                month, year, startDay +1\ );
*/
        printf("\nYour calender for month %d and year %d is ...\n\n", month, year);
        printCalendar(startDay, daysInMonth(month, year));

    }while( more() );

    return 0;
}

You may also like to check this student help link for an example of todays local date and time in C/C++ :

http://www.dreamincode.net/forums/index.php?showtopic=64722&st=0&p=422833&#entry422833
Title: Re: New thread especially for students of C and C++
Post by: David on November 29, 2008, 02:36:25 PM
Back to pointers ... in C ...

Here is a mini-tutorial on arrays in C ...

Code: [Select]
/*
    I am trying to add the contents of this array after entering the values
    of the array ... but I am confused as to how to do this, please help me.
*/
#include <stdio.h>

#define SIZE 5

int main()
{
    float dAry[ SIZE ];
    float sumInput =0, sumAry =0;
    int i, j, numGood;
    printf( "Enter %d floating point numbers ...\n", SIZE );
    for( i=0;  i<SIZE; ++i )
    {
        printf( "dAry[%d] = ", i );
        numGood = scanf( "%f", &dAry[i] );
        if( numGood==1 ) sumInput += dAry[i];
        else
        {
            --i;
            puts("Bad data ... enter numbers only.");
        }
        while( getchar() != '\n' ) ; /* flush stdin ... */
    }

    for( j=0; j<i; ++j ) sumAry += dAry[j];
   
    printf( "sumInput=%f and sumAry=%f", sumInput, sumAry );

    printf("\nPrint 'Enter' to continue ... ");
    getchar();
    return 0;
}


NOTE in the following code that 'ary' and 'pAry' both point to arrays of type 'int' ... with this important difference:

sizeof(ary) will give the number of bytes allocated for the array of int's in memory ... In the example here of 5 int's and if each int uses 4 bytes ... then sizeof(ary) returns 20

but

sizeof(pAry) will return 4, if in a 32 bit addressing environment ... since all pointer variables, and pAry was declared as a pointer to an int, use 4 bytes to hold an address, in a 32 bit addressing system.

Code: [Select]
#include <stdio.h>
#include <stdlib.h>

void show( int a[], int size )
{
    printf("\n\nArray  syntax  ... ");
    int i;
    for(i=0; i<size; ++i) printf("%d ", a[i]);
}

/* alt. version of above ... using a pointer syntax ... */
void show2( int *a, int size )
{
    printf("\n\nPointer syntax ... ");
    int i;
    for(i=0; i<size; ++i) printf("%d ", *a++);
}



int main()
{
    /*
        create an array in (static) memory with intitial values
        ary[0]=1, ary[1]=2, ..., ary[4]=5 
    */
    int ary[] ={1,2,3,4,5};
    int len_ary = sizeof(ary)/sizeof(ary[0]);
    show( ary, len_ary);
    show2( ary, len_ary);
   
    /*
        Create an array in dynamic memory
        with initial int values pAry[0]..pAry[4] undefined ...
        Or using alt. syntax, these initial int values are undefined ...
        *pAry, *(pAry+1), *(pAry+2),  *(pAry+3), *(pAry+4)

        Allocate (dynamic) memory for 5 int's.
        If an int is 32 bits or 4 bytes ...
        5 int's will need 5*4 = 20 (bytes).
        This new memory is 'cast' as type
        'a pointer to int', the same type as pAry.
    */
    int *pAry = (int*) malloc(sizeof(int)*len_ary);
   
    /* give it some values ... */
    int i;
    for(i=0; i<len_ary; ++i) pAry[i] = (i+1)*(i+1);
     
    show(pAry, len_ary); 
    show2(pAry, len_ary);
       
    /* NOTE: ary and pAry both point to arrays of type int */
       
    printf("\n\nPress 'Enter' to continue ... ");
    getchar();
    return 0;
}


Now here is an example of a 2DArrays with dynamic memory.  Note that it uses 'a pointer to a pointer to an int'.

This example demos the addition of two matrices.

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct twoDArray
{
    int **p;
    int rows;
    int cols;
}matrix;

typedef matrix * pMatrix;

/*
   'createMatrix' returns a pointer to a C struct ...
   The 'struct' holds a 'pointer to a pointer to int' for the 2-D array ... 
   AND ALSO holds the number of rows and cols for the 2-D array.
*/
pMatrix createMatrix(int, int);
void showMatrix(pMatrix);
void freeMatrix(pMatrix);


int main()
{
    int r = 2, c = 4;
    int count = r*c*2;
    pMatrix pM1, pM2, pM3;

    pM1 = createMatrix(r, c);
    /* fill in some values ... */
    int i, j;
    for(i=0; i<pM1->rows; ++i)
        for(j=0; j<pM1->cols; ++j)
            pM1->p[ i ][ j ] = count--;

    pM2 = createMatrix(r, c);
    /* fill in some values ... */
    for(i=0; i<pM2->rows; ++i)
        for(j=0; j<pM2->cols; ++j)
            pM2->p[ i ][ j ] = count--;
           
    pM3 = createMatrix(r, c);
    /* sum first two matrices ...*/
    for(i=0; i<pM2->rows; ++i)
        for(j=0; j<pM2->cols; ++j)
            pM3->p[ i ][ j ] = pM2->p[ i ][ j ] + pM1->p[ i ][ j ];
   
    puts("");

    /* showTwoDArray( pAry ); */
    puts("  Matrix1");
    showMatrix(pM1);
    puts("\n+ Matrix2");
    showMatrix(pM2);
    puts("\n= Matrix3");
    showMatrix(pM3);
    puts("\nfree3");
    freeMatrix(pM3);   
    puts("\nfree2");
    freeMatrix(pM2);   
    puts("\nfree1");
    freeMatrix(pM1);   
    printf("\n\nPress 'Enter' to continue ... ");
    getchar();
    return 0;
}

pMatrix createMatrix(int numrows, int numcols)
{
     pMatrix pM = (pMatrix)malloc(sizeof(matrix));
     assert(pM);

     pM->p = (int**)malloc(sizeof(int*)*numrows);
     assert(pM->p);
     int i; // j;
     for(i=0; i<numrows; ++i)
     {
          pM->p[ i ] = (int*)malloc(sizeof(int)*numcols);       
          assert(pM->p[ i ]);
          //for( j = 0; j < numcols; ++j )
              //pM->p[i][j] = 0; /* initialize all to zero  */                 
     }
     pM->rows = numrows;
     pM->cols = numcols;
     return pM;
}

void showMatrix(pMatrix pM)
{
    int i, j;
    for(i=0; i < pM->rows; ++i)
    {
         for(j=0; j < pM->cols; ++j)
              printf("%4d ", pM->p[ i ][ j ]);           
         puts("");
    }       
}

void freeMatrix(pMatrix pM)
{
     int i;
     for(i = pM->rows-1; i >= 0 ; --i)
     {
          printf( "pM->p[%d] = %d . ", i, (int)pM->p[ i ] );
          free( pM->p[ i ] );
     }   
     
     printf( "pM->p = %d . ", (int)pM->p );             
     free( pM->p );

     printf( "pM = %d",  (int)pM );
     free( pM );   
}


This example demos the multiplication of two matrices.

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct twoDArray
{
    int **p;
    int rows;
    int cols;
}matrix;

typedef matrix * pMatrix;

/*
   'createMatrix' returns a pointer to a C struct ...
   The 'struct' holds a 'pointer to a pointer to int' for the 2-D array ... 
   AND ALSO holds the number of rows and cols for the 2-D array.
*/
pMatrix createMatrix(int, int);
void showMatrix(pMatrix);
void freeMatrix(pMatrix);


int main()
{
    int r = 2, c = 4;
    int count = r*c*2;
    pMatrix pM1, pM2, pM3;

    pM1 = createMatrix(r, c);
    /* fill in some values ... */
    int i, j, k;
    for(i=0; i<pM1->rows; ++i)
        for(j=0; j<pM1->cols; ++j)
            pM1->p[ i ][ j ] = count--;

    pM2 = createMatrix(c, r);
    /* fill in some values ... */
    for(i=0; i<pM2->rows; ++i)
        for(j=0; j<pM2->cols; ++j)
            pM2->p[ i ][ j ] = count--;
           
    pM3 = createMatrix(r, r);
    /* x first two matrices ...*/
    for(i=0; i<r; ++i)
        for(j=0; j<c; ++j)
            for(k=0; k<c; ++k)
                pM3->p[ i ][ j ] += pM1->p[ i ][ k ] * pM2->p[ k ][ j ];
   
    puts("");

    /* showTwoDArray( pAry ); */
    puts("  Matrix1");
    showMatrix(pM1);
    puts("\nx Matrix2");
    showMatrix(pM2);
    puts("\n= Matrix3");
    showMatrix(pM3);
    puts("\nfree3");
    freeMatrix(pM3);   
    puts("\nfree2");
    freeMatrix(pM2);   
    puts("\nfree1");
    freeMatrix(pM1);   
    printf("\n\nPress 'Enter' to continue ... ");
    getchar();
    return 0;
}


pMatrix createMatrix(int numrows, int numcols)
{
     pMatrix pM = (pMatrix)malloc(sizeof(matrix));
     assert(pM);

     pM->p = (int**)malloc(sizeof(int*)*numrows);
     assert(pM->p);
     int i, j;
     for(i=0; i<numrows; ++i)
     {
          pM->p[ i ] = (int*)malloc(sizeof(int)*numcols);       
          assert(pM->p[ i ]);
          for( j = 0; j < numcols; ++j )
              pM->p[i][j] = 0; /* initialize all to zero  */                 
     }
     pM->rows = numrows;
     pM->cols = numcols;
     return pM;
}

void showMatrix(pMatrix pM)
{
    int i, j;
    for(i=0; i < pM->rows; ++i)
    {
         for(j=0; j < pM->cols; ++j)
              printf("%4d ", pM->p[ i ][ j ]);           
         puts("");
    }       
}

void freeMatrix(pMatrix pM)
{
     int i;
     for(i = pM->rows-1; i >= 0 ; --i)
     {
          printf( "pM->p[%d] = %d . ", i, (int)pM->p[ i ] );
          free( pM->p[ i ] );
     }   
     
     printf( "pM->p = %d . ", (int)pM->p );             
     free( pM->p );

     printf( "pM = %d",  (int)pM );
     free( pM );   
}


Now here is a Jacobi Iteration Solution-Method for strictly diagonally dominant square matrices ... (to solve a system of linear equations.)

Code: [Select]
/* was int ,,, now 2014-02-16 ,,, char SHOW_ITERS = 0; */ /* defaults to NO ... */

/* see credits to original source below ... this version features all */
/* dynamic arrays ... some input data validation ... results checking ... and */
/* also 2 (major) errors, in the code found on 2009-03-24 on the web, were fixed */
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

/* globals ... */

#define HEADER "**********************************************************\n" \
               "             JACOBI ITERATION SOLUTION METHOD             \n" \
               "    (for strictly diagonally dominant square matrices)    \n" \
               "**********************************************************\n"
#define EPSILON 0.0000001
#define MAX_PRINT_ITERS 333

char SHOW_ITERS = 0; /* defaults to NO ... */

/* input matrix elements */
void getInput( int, double** mat );
/* check if diagonally dominant */
int checkIfDD( int, double** mat );
/* calculate and display answers */
void jacobiCalcDisplay( int, double** mat );

double dotProd( int num, double* v1, double* v2 );

void showXcheck( int num, double** mat, double* v1, double* v2 );

int checkFlags( int num, int flags[] );

/* free dynamic memory occupied by 2D square matrix */
void freeMat( int num, double** sqMat );

int more()/* defaults to yes ... */
{
    int reply ;
    printf("\nMore ... (y/n) ? ");
    reply = getchar();
    if( reply != '\n' )
        while( getchar() != '\n' ) ; /* flush stdin ... */
    putchar( '\n' );
    return !(reply=='n' || reply=='N');
}



int main()
{
    double **mat;
    int num;

    do
    {
        puts( HEADER );

        printf( "Show each iteration result 1=Yes, 0=No ? " );
        scanf("%c", &SHOW_ITERS);
        if( SHOW_ITERS != '\n' )
            while( getchar() != '\n' ) ; /* flush stdin ... */

        if( !(SHOW_ITERS=='1'||SHOW_ITERS=='y'||SHOW_ITERS=='Y') )
        {
            puts("Ok ... 'No' it is ...");
            SHOW_ITERS =0;
        }
        else puts("Ok ... 'Yes' show iterations ... (This may take some time.)");

        printf("\nTo solve a system of linear equations ...\n");
        printf("Enter the number of 'equations/unknowns' to find: ");
        num = 0;
        scanf("%d", &num); /* num of unknowns */
        while( getchar() != '\n' ) ; /* flush stdin ... */

        mat = NULL;
        if( num>0 && (mat = (double**) malloc(num*sizeof(double*))) )
        {
            getInput(num, mat);
            if( checkIfDD(num, mat) ) jacobiCalcDisplay(num,mat);
            else freeMat(num, mat);
        }
        else
        {
            puts("Invalid choice or memory available was exceeded ... Try again.");
            if( mat != NULL ) free( mat );
        }
    }while( more() );
    return 0;
}


void getInput( int numUnKnowns, double** mat )
{
    int i, j;

    printf
    (
        "\nEnter values for the specified row and column below ...\n"
        "(The last column is the value for the RHS of the equation.)\n"
    );
   
    for( i = 0 ; i < numUnKnowns ; i++ )
    {
        mat[i] = (double*) malloc( (numUnKnowns+1)*sizeof(double) );
        puts("");
        for( j = 0 ; j < numUnKnowns+1 ; j++ )
        {
            printf("matrix[%d][%d] : ", i, j);
            if( scanf("%lf", &mat[i][j]) != 1 )
            {
                --j;
                puts("Bad entry ... try again with a 'real' number.");
            }
            while( getchar() != '\n' ) ; /* flush stdin ... */
        }
    }

    printf("\nThe matrix entered:\n\n");
    for( i = 0 ; i < numUnKnowns ; i++ )
    {
        for( j = 0 ; j < numUnKnowns+1 ; j++ ) printf("%+9f ", mat[i][j]);
        puts("");
    }

    printf("\nPress 'Enter' to start iteration ... ");
    getchar();
}

/* Check if the matrix entered is strictly diagonally dominant */
int checkIfDD( int numUnKnowns, double** mat )
{
    int   m, n, dd = 0;
    double* chkdd;
    double* sumdd = (double*) malloc( numUnKnowns*sizeof(double) );
    chkdd = (double*) malloc( numUnKnowns*sizeof(double) );

    for( m = 0 ; m < numUnKnowns ; m++ )
    chkdd[m] = sumdd[m] = 0; /* all set to zero ... */

    printf("\nChecking if the matrix is (strictly) diagonally dominant...");

    for( m = 0 ; m < numUnKnowns ; m++ )
    {
        for( n = 0 ; n < numUnKnowns ; n++ )
            sumdd[m] += fabs(mat[m][n] );
        sumdd[m] -= fabs(mat[m][m]);
        chkdd[m] = fabs(mat[m][m]);

        if(chkdd[m] > sumdd[m])
        {
            printf("\n%f > %f",chkdd[m],sumdd[m]);
            dd++;
        }
        else printf("\n%f !> %f",chkdd[m],sumdd[m]);
    }

    if(dd == numUnKnowns)
    {
        printf
        (
            "\nYES ..."
            "\nThe matrix is (strictly) diagonally dominant."
            "\nPress 'Enter' to continue with 'Jacobi Iterative Method'...\n"
        );
        getchar();
    }
    else
    {
        printf
        (
            "\nNO ..."
            "\nThe matrix is NOT (strictly) diagonally dominant ... so STOP!"
            "\n(But ... consider exchanging rows in the matrix "
            "and then to try again.)"

            "\n\nPress 'Enter' to continue ... "
        );

        getchar();
        free( sumdd );
        free( chkdd );
        return 0; /* false */
    }

    free( sumdd );
    free( chkdd );
    return 1; /* true */
}

/* uses global SHOW_ITERS ... */
void jacobiCalcDisplay( int numUnKnowns, double** mat )
{
    int* flag;
    int i, j, counter = 0;
    double* res;
    double* var = (double*) malloc( numUnKnowns*sizeof(double) );
    res = (double*) malloc( numUnKnowns*sizeof(double) );
    flag = (int*) malloc( numUnKnowns*sizeof(int) );

    for(i = 0 ; i < numUnKnowns ; i++ )
    var[i] = res[i] = flag[i] = 0;
    printf("The initial value of each array element was set to zero ...\n\n");

    printf( "*********************\n");
    printf( "START CALCULATING ...\n");
    printf( "*********************\n");

    do
    {
        counter++;
        /* for each iteration keep a copy of the old results ... */
        for(i = 0 ; i < numUnKnowns ; i++ )
        {
            var[i] = res[i];
        }

        if( SHOW_ITERS ) printf("\nIteration number %d ...\n", counter);

        for(i = 0 ; i < numUnKnowns ; i++ ) /* calculation */
        {
            res[i] = mat[i][numUnKnowns];
            for(j = 0 ; j < numUnKnowns ; j++ )
                res[i] = res[i] - mat[i][j]*var[j] ;

            res[i] = res[i] + mat[i][i]*var[i] ;
            res[i] = res[i] / mat[i][i] ;
            if( SHOW_ITERS ) printf("%c = %f\n", 'a'+i, res[i]);
            if( fabs(res[i] - var[i]) < EPSILON ) /* stop condition */
            flag[i]++;

            if( counter==MAX_PRINT_ITERS) SHOW_ITERS = 0;
        }
       
    }while( !checkFlags( numUnKnowns, flag ) );

    printf( "\n********************************\n");
    printf( "The RESULTS of %d ITERATIONS ... \n", counter);
    printf( "********************************\n");

    /*  cross check ...*/

    for( i = 0 ; i < numUnKnowns ; i++)
        var[i] = dotProd( numUnKnowns, mat[i], res );

    showXcheck( numUnKnowns, mat, res, var );

    /* show sol'n vector (again) ... and free up all dynamic memory  */

    printf("\nSolution vector ...\n");
    for( i = 0 ; i < numUnKnowns ; i++)
    {
        printf("%c = %+f\n", 'a'+i, res[i]);
        free(mat[i]);
    }
    free( mat );
    free( flag );
    free( res );
    free( var );
}

int checkFlags( int num, int flags[] )
{
    int i;
    for( i=0; i<num; ++ i)
        if( flags[i] == 0 ) return 0;
    return 1;
}

double dotProd( int num, double* v1, double* v2 )
{
    int i;
    double sum =0;
    for( i=0; i<num; ++i ) sum += v1[i]*v2[i];
    return sum;
}

void showXcheck( int num, double** mat, double* v1, double* v2 )
{
    int i, j;
    puts("\nCross checking ... \nMatrix times sol'n vector ="
         " cal. vector vs. original RHS vector");
    for( i = 0 ; i < num ; i++)
{
        printf("|");
        for( j =0 ; j < num ; j++ )
            printf("%+9f ", mat[i][j] );
        printf("| |%+9f| |%+9f|vs|%+9f|\n", v1[i], v2[i], mat[i][num]);
    }
}


void freeMat( int num, double** sqMat )
{
    int i;
    for( i=num-1; i>=0; --i )
        free( sqMat[i] );
    free( sqMat );
}


/*
Title: Jacobi Iteration for a system of linear equations ...

Consider a 4 unknowns linear system

7a - 2b +  c + 2d =  3
2a + 8b + 3c +  d = -2
-a      + 5c + 2d =  5
     2b -  c + 4d =  4

In my program, when asked for number of unknown, just enter 4.

When asked for value enter, it should be like this referred to the above linear system:

_____________________________________________
                JACOBI ITERATION
         AUTOMATIC ITERATION CALCULATOR
                 PUBLIC VERSION
_____________________________________________
Current available unknown calculation is from 1 until 26.
Please enter the number of unknown: 4
Enter values for the specified row and column below,
Row 1, Column 1 : 7
Row 1, Column 2 : -2
Row 1, Column 3 : 1
Row 1, Column 4 : 2
Row 1, Column 5 : 3
Row 2, Column 1 : 2
Row 2, Column 2 : 8
Row 2, Column 3 : 3
Row 2, Column 4 : 1
Row 2, Column 5 : -2
Row 3, Column 1 : -1
Row 3, Column 2 : 0
Row 3, Column 3 : 5
Row 3, Column 4 : 2
Row 3, Column 5 : 5
Row 4, Column 1 : 0
Row 4, Column 2 : 2
Row 4, Column 3 : -1
Row 4, Column 4 : 4
Row 4, Column 5 : 4

Next is just calculation and result will print on screen.

by AlexTan
Segamat Baru 85000
Segamat, Johor
Malaysia
*/

You may also want to check here ...

http://www.dreamincode.net/forums/topic/64817-solving-linear-equations-using-iteration/
Title: Re: New thread especially for students of C and C++
Post by: David on November 29, 2008, 08:13:31 PM
C++ Student class ... a very common student problem ...

Update: moved to here ...

http://developers-heaven.net/forum/index.php/topic,2585.0.htm


A demo simpler class Student ...

Code: [Select]
// a very simple student class that also demos overloading the << operator
#include <iostream>

using namespace std;

class student

public:
    student(string n = "", string d = "") : name(n), department(d) {}
    string get_name() const { return name; }
    string get_department() const { return department; }
    void set_name(const string& n) { name = n; }
    void set_department(const string& d) { department = d; }
private:
    string name;
    string department;
};

ostream& operator << (ostream& os, const student& s )
{
    return os<<s.get_name()<<", "<<s.get_department();
}

int main()
{
  student s( "Billy Kid", "Zoology" );
  student t, w;
  t = s;
  w.set_name( "Bobby Stone" );
  w.set_department( "Geology" );
  t.set_department( w.get_department() );
    cout << s << endl << t << endl << w << endl;
   
  system("pause");
return 0;
}


And an Entry class ...

Code: [Select]
// using a vector to hold objects of class Entry
// a 'shell'... for a contact/address data base

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cctype>

using namespace std;

class Entry
{
    friend istream&operator>>(istream&, Entry&);
    friend ostream&operator<<(ostream&, Entry&);
   
public:
   
    // default constructor
    Entry() {}
   
    // constructors
    Entry(string N, string A1, string A2, string P)
    {
        Person=N; Add1=A1; Add2=A2; Phone=P;
    }
   
    void setPerson(string temp) {Person=temp;}
    void setAdd1(string temp) {Add1=temp;}
    void setAdd2(string temp) {Add2=temp;}
    void setPhone(string temp) {Phone=temp;}
   
    // accessors
    string getPerson() {return(Person);}
    string getAdd1() {return(Add1);}
    string getAdd2() {return(Add2);}
    string getPhone() {return(Phone);}
   
private:
    string Person, Add1, Add2, Phone;
};

// friend function to overload the >> operator for input
istream &operator>>(istream &input, Entry &e)
{
   string temp[5];
   for(int i=0; i<5; i++) getline(input, temp[i], '\n');
   Entry e_temp(temp[0],temp[1],temp[2],temp[3]);
   e=e_temp;
   return(input);
}

// friend function to overload the << operator for output
ostream&operator<<(ostream &output, Entry &e)
{
    output << e.Person<<endl
           << e.Add1<<endl
           << e.Add2<<endl
           << e.Phone<<endl;
    return(output);
}

// prototypes of functions used in 'main'
void appendBook(vector< Entry>&);           // add to book from keyboard ...
void printBook(vector< Entry >&);           // print the current book to screen
void readBook(const char[], vector< Entry >&);  // read book from file
void writeBook(const char[], vector< Entry >&); // write the current book to file

// Globals ...
const char line[] = "-----------------------------------";
const char fName[] = "MyBBook.dat";

int main()
{
    vector < Entry > Book;          // construct a new vector Book for 'entries'
    appendBook(Book);               // add to Book from keyboard ...
   
    cout << "\nHere is the 'book' you just entered ...\n";
    cout << line << endl;
    printBook(Book);                // show book on screen
    writeBook(fName, Book);         // write Book to file
    cout << "\nThe 'last' record is " << Book.size() << "\n\n";
    system("pause");
   
    vector< Entry > v;
    Book = v;                       // set to empty ...
    readBook(fName, Book);          // read Book from file
    cout << "\nHere is the 'book'  read  from file ...\n";
    cout << line << endl;
    printBook(Book);
    cout << "The 'last' record is " << Book.size() << "\n\n";
    system("pause");
}

// definitions of functions used in 'main'
string takeIn(string message)
{
    cout<<message<<": ";
    string inStr;
    getline(cin, inStr, '\n');
    return(inStr);
}

void appendBook(vector< Entry > &b)
{
    Entry e;
    do
    {
        e.setPerson(takeIn("Enter name            "));
        e.setAdd1  (takeIn("Enter address 1st line"));
        e.setAdd2  (takeIn("Enter address 2nd line"));
        e.setPhone (takeIn("Enter phone           "));
        b.push_back(e);
       
    }while( tolower(takeIn("More y/n ?            ")[0]) != 'n' );
}

void printBook(vector< Entry > &b)
{
    for(size_t i=0; i<b.size(); ++i) cout << b[i] << endl;
}

void writeBook(const char f[], vector< Entry > &b) // write the current book to file
{
    // 5 lines of data are written for each 'entry' (includes 5th blank line)
    ofstream OUTFILE( f );
    for(size_t i=0; i<b.size(); i++) OUTFILE << b[i] << endl;
    OUTFILE.close();
}

void readBook(const char f[], vector< Entry > &b)
{
    ifstream INFILE( f );
    Entry e;
    while(INFILE >> e) { b.push_back(e); }
    INFILE.close();
}
Title: Re: New thread especially for students of C and C++
Post by: David on January 14, 2009, 03:06:53 PM
A C way to handle student records using a linked list of C struct ... ("Student GRADES MANAGEMENT SYSTEM")

moved/updated ... now appears later here (near the end, at present ... click on the next link to jump to there right now:)

http://developers-heaven.net/forum/index.php/topic,106.msg273.html#msg273


Title: Re: New thread especially for students of C and C++
Post by: David on January 14, 2009, 03:08:44 PM
The following function definitions belong to the program above ...

Code update/moved as per notice on above page ...
Title: Re: New thread especially for students of C and C++
Post by: David on January 14, 2009, 03:35:20 PM
A similar program to the above coded in HLA ....

Code: [Select]
program studentList_test;

// A mini demo of student record keeping
// using an HLA 'list' type student record structure.
// Each new student is inserted in order of unique
// student ID number (an HLA int32).

#include( "stdlib.hhf" )

?@nodisplay:=true;
const
fName: string:="studentInfo.txt";
max: int32:= 3; // using a small number here to keep this demo simple

type
Student:
record
id: int32;
sname: string;
marks: int32[max];
next: pointer to Student;
endrecord;

pStudent: pointer to Student;

static
pHead: pStudent:= NULL;
pTail: pStudent:= NULL; // used by append( pS:pStudent );
fileFlag: boolean:= false;
cnt: int32:= 0;

const
header1: string:= "STUDENT GRADES MANAGEMENT SYSTEM" nl;
header2: string:= "1. (I)nitialize student record list in RAM" nl
                  "2. (A)dd i.e. insert in order, by ID, a NEW student record" nl
                  "3. view/(D)elete a student record" nl
                  "4. view/(E)dit a student record" nl
                  "5. (S)how all student records (as inserted BY ID)" nl
                  "6. show all student records sorted by (N)ame" nl
                  "7. (F)ile all student records presently in RAM" nl
                  "8. (R)ead all student records on file into RAM" nl
                  "9. e(X)it" nl;

// Return a pointer to the Student if ID found in list; else return NULL
procedure pID( n:int32 ); @returns("eax");
begin pID;
push( ecx );

mov( pHead, eax ); // get address of pHead into eax
mov( n, ecx ); // using ecx to hold ID number to check in 'n'
   
//while( eax != NULL ) do
while1:
cmp( eax, NULL );
je endwhile1;
cmp( (type Student[eax]).id, ecx );
je endit; // return address eax, since id number entered was already use

// else ... get next pointer to compare
mov( (type Student[eax]).next, eax );
//endwhile;
jmp while1;
endwhile1:
       
// If reach here ... then id number not in list so ...
mov( NULL, eax ); // return NULL;
   
 endit:
pop( ecx );
end pID;

// A function to compare two Student records to permit sorting by ID number ...
procedure studentCmp( pS1:pStudent; pS2:pStudent ); @returns("eax");
begin studentCmp;
push( ebx ); push( ecx );
mov( pS1, ebx );
mov( pS2, ecx );
mov( (type Student[ecx]).id, ecx );
if( (type Student[ebx]).id < ecx ) then
mov( 1, eax );
else
mov( 0, eax );
    endif;
pop( ecx ); pop( ebx );
end studentCmp;

// insert new Student in list with id's in proper order
procedure insert( pS:pStudent );
begin insert;
push( eax ); push( ebx ); push( ecx );
mov( pS, ebx );

mov( pHead, ecx ); // Get a working copy of pHead in ecx
    // Firstly, we handle the case where 'this' should be the first element.
if( pHead == NULL || studentCmp(pS, pHead) ) then
// So ... it now becomes the first element ...
// mov( pHead, ecx );
mov( ecx, (type Student[ebx]).next );  // old pHead becomes 2nd in list ...
mov( ebx, pHead); // pHead = pS; ... this pS becomes the head of the list
else // If here ... search the linked list for the right location ...
// mov( pHead, ecx ); // Get a working copy of pHead in ecx
// Start comparing with element AFTER 'ecx' ... i.e. the 'next in' ...
while( (type Student[ecx]).next != NULL && studentCmp((type Student[ecx]).next, pS) ) do
mov( (type Student[ecx]).next, ecx ); // Traverse the list ...
endwhile;

// Ok now to insert after 'working copy' by relinking the pointers (similar to above)
// ( Includes inserting at end position ... where (type Student[ecx]).next == NULL )
     
mov( (type Student[ecx]).next, eax );
mov( eax, (type Student[ebx]).next ); // Inserted 'pS' is linked to 'upstream element'
mov( ebx, (type Student[ecx]).next ); // Now 'ecx' is updated to link to the new 'pS' element
endif;

// Update global variables.
inc( cnt );
mov( true, fileFlag );
pop( ecx ); pop( ebx ); pop( eax );
end insert;


procedure get_i( message:string ); @returns( "eax" );
begin get_i;
forever
try
stdout.put( message, ": " );
stdin.flushInput();
stdin.geti32();
unprotected
break;
exception( ex.ValueOutOfRange )
stdout.puts( "Out of range ... " );
exception( ex.ConversionError );
stdout.puts( "Illegal numeric value ... "  );
anyexception
stdout.puts( "Illegal entry, integers only ... "  );
endtry;
endfor;
end get_i;


procedure addInsertNew; // ... and insert in the proper place in the list.
var
tmpStr: string;
good: boolean;
begin addInsertNew;
push( eax ); push( ebx ); push( ecx );

get_i( "Enter ID (0 to abort)" );
cmp( eax, 0 );
je endit;

mov( eax, ebx ); // get a copy of ID into ebx
if( pID(ebx) ) then // i.e. if pointer returned is NOT NULL, the id IS used
stdout.put( nl "ID ", (type int32 ebx), " is already taken.", nl );
jmp endit; // Exit right now ...
endif;
   
mov( mem.alloc(@size(Student)), ecx ); 
mov( ebx, (type Student[ecx]).id ); // pS->id = ID;
   
stdout.puts( "Enter Name (Last, First): " );
stdin.flushInput();
stdin.a_gets();
mov( eax, (type Student[ecx]).sname );

for( mov( 0, ebx ); ebx<max; inc( ebx ) ) do
push( ebx ); push( ecx ); // preserve across exception handling
mov( str.alloc( 32 ), tmpStr ); // get a tmp string to hold 'input prompt message'
inc( ebx ); // for 1,2,3... instead of 0,1,2....
str.put( tmpStr, "Enter mark ", (type int32 ebx):3 );
//dec( ebx ); // restore ebx to 0,1,2... // see pop( ebx ) ... below
get_i( tmpStr ); // returns int32 in eax
pop( ecx ); pop( ebx ); // preserved across exception handling
mov( eax, (type Student[ecx]).marks[ebx*4] ); // update marks with this mark
str.free( tmpStr );
endfor;
   
insert( ecx ); // Set up links to this node
   
 endit:
pop( ecx ); pop( ebx ); pop( eax );
end addInsertNew;


procedure view( pS:pStudent );
begin view;
push( eax ); push( ebx ); push( ecx );
mov( pS, ecx );
stdout.put
(
"ID number: ", (type Student[ecx]).id:-4, 
" Name: ", (type Student[ecx]).sname:-20, " Marks: "
);
for( mov( 0, eax ); eax < max; inc( eax ) ) do
mov( eax, ebx );
shl( 2, ebx ); // ebx*4
stdout.put( (type Student[ecx]).marks[ebx], " " );
  endfor;
  pop( ecx ); pop( ebx ); pop( eax );
end view;


procedure showAll;
begin showAll;
if( pHead == NULL ) then
stdout.puts( nl "No records in memory at present." nl );
else
push( ecx );
mov( pHead, ecx );
while( ecx != NULL ) do
view( ecx );
stdout.newln();
mov( (type Student[ecx]).next, ecx );
endwhile;
stdout.newln();
pop( ecx );
endif;
end showAll;


procedure del( pS:pStudent );
begin del;
push( eax ); push( ebx ); push( ecx ); push( edx );

/* Handle special case of 'first in list' */
mov( pS, ecx );
if( ecx == pHead ) then
mov( (type Student[ecx]).next, pHead );//phead = pS->next;
//str.free( (type Student[ecx]).sname ); //free( pS->first );
//mem.free( ecx );//free( pS );
jmp endit; //return;   
endif;
   
/* Else not first in list, so ... */
   
mov( pHead, ebx );/* set p to this initial value to start loop */
   
/* Now find the pointer to the previous record. */
while( ebx != pS ) do
mov( ebx, edx );/* pp holds previous pointer value ... */
mov( (type Student[ebx]).next, ebx );/* set to next pointer in link list chain */
endwhile;
   
/*
Now we have a pointer to the previous student record, so ...
we can now point that previous record to one past this pS record           
*/
mov( (type Student[ecx]).next, eax );
mov( eax, (type Student[edx]).next );
   
/* Now free the memory for this record and update the globals ... */
 endit:
str.free( (type Student[ecx]).sname );
mem.free( ecx );
 
dec( cnt );
if( cnt>0 ) then
mov( true, fileFlag );
else
mov( false, fileFlag );
endif;
   
pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end del;


procedure viewDel;
begin viewDel;
push( eax ); push( ebx ); push( ecx );

get_i( "Enter the id number to View/Delete" );
mov( eax, ebx ); // ID in ebx
   
mov( pID( ebx ), ecx ); /* See if pointer exists; i.e. get value or NULL */

if( cl ) then /* i.e. if pointer returned above was not NULL ... */
view( ecx );
stdout.puts( nl "Delete (y/n) ? ");
stdin.flushInput();
stdin.getc();
if( al=='y' || al=='Y' ) then
del( ecx );
endif;
else
stdout.put( nl, "Student with ID number ", (type int32 ebx), " not found.", nl );
endif;
pop( ecx ); pop( ebx ); pop( eax );
end viewDel;


procedure delAll;
begin delAll;
push( ebx ); push( ecx );

mov( pHead, ecx );
while( ecx != NULL ) do
mov( (type Student[ecx]).next, ebx ); // Get a pointer to the next record.
str.free( (type Student[ecx]).sname );
mem.free( ecx );
mov( ebx, ecx ); // Update ecx ...
endwhile;
   
// Update globals ...
mov( NULL, pHead );
mov( 0, cnt );
mov( false, fileFlag );

pop( ecx ); pop( ebx );
end delAll;

/* Note: calling delAll() here ... will re-set globals as above ... */
procedure init;
begin init;
push( eax );

if( pHead != NULL ) then
stdout.puts( nl "Do you wish to overwrite the records in memory y/n ? " );
stdin.flushInput();
stdin.getc();
if( al=='y' || al=='Y' ) then
delAll(); /* re-sets globals ... */
else
if( cnt==1 ) then stdout.puts( "1 student record was left intact in memory." );
else stdout.put( cnt, " student records were left intact in memory.", nl );
endif;
endif;
else
stdout.puts( nl "There were no records in memory to clear." nl );
endif;
   
pop( eax );
end init;


procedure viewEdit;
begin viewEdit;
push( eax ); push( ebx ); push( ecx );

get_i( "Enter the id number to View/Edit" );
mov( eax, ebx ); // ID in ebx
   
mov( pID( ebx ), ecx ); /* See if pointer exists; i.e. get value or NULL */

if( ecx ) then /* i.e. if pointer returned above was not NULL ... */
view( ecx );
stdout.puts( nl "Edit (y/n) ? ");
stdin.flushInput();
stdin.getc();
if( al=='y' || al=='Y' ) then
del( ecx );
mov( true, fileFlag );
addInsertNew();
endif;
else
stdout.put( nl, "Student with ID number ", (type int32 ebx), " not found.", nl );
endif;
   
pop( ecx ); pop( ebx ); pop( eax );
end viewEdit;


procedure writeFile; @returns( "eax" );
var
fp: dword;
begin writeFile;
push( ebx ); push( ecx ); push( edx );
 
mov( pHead, ecx );
if( ecx==NULL ) then
stdout.puts( nl "No records available ... so NO records written to file." nl );
mov( 0, eax );
jmp endit;
endif;

fileio.openNew( fName );
mov( eax, fp );

mov( 0, eax ); /* to count the records actually written to the file */
while( ecx!=NULL ) do
fileio.put
(
fp,
(type Student[ecx]).id, nl,
(type Student[ecx]).sname, nl
);
for( mov( 0, edx ); edx < max; inc( edx ) ) do
mov( edx, ebx );
shl( 2, ebx ); // ebx*4
fileio.put( fp, (type Student[ecx]).marks[ebx], nl );
  endfor;
       
mov( (type Student[ecx]).next, ecx );
inc( eax );
endwhile;
fileio.close( fp );
mov( false, fileFlag );
   
 endit:
pop( edx ); pop( ecx ); pop( ebx );
end writeFile;

// uses globals pHead and pTail ...
procedure append( pS:pStudent );
begin append;
push( ebx ); push( ecx );

mov( pS, ecx );
if( pHead != NULL ) then
mov( pTail, ebx ); // get tail address into ebx
mov( ecx, (type Student[ebx]).next ); // point old tail to next
mov( NULL, (type Student[ecx]).next ); // point new tail next to NULL
mov( ecx, pTail ); // update address for tail
else // is first record ... so
mov( ecx, pHead ); // get new address for pHead
mov( NULL, (type Student[ecx]).next ); // point next to NULL
mov( ecx, pTail ); // update address for tail
endif;

pop( ecx ); pop( ebx );
end append;


procedure readFile; @returns( "eax" );
var
fp: dword;
count: int32;
exists: boolean;
tmpStr: string;
begin readFile;
push( ebx ); push( ecx ); push( edx );

str.alloc( 32 );
mov( eax, tmpStr );

mov( 0, count );
try
fileio.open( fName, fileio.r );
mov( eax, fp );
mov( true, exists );
anyexception
mov( false, exists );
stdout.put
(
nl "Error opening file ", fName, "." nl
"Perhaps it hasn't been created yet?" nl
"Press 'Enter' to continue ... "
);
stdin.readLn();
endtry;

cmp( exists, false );
je endit;
   
init(); // BUT!!! may need to delete any records in memory first.
/*
NOTE: we need pHead to be equal to NULL in the following ...
to be permitted to set pHead ... 
to point to the first student record in memory (read in from the file).
*/
if( pHead != NULL ) then // then exit with message ...
if( cnt==1 ) then
stdout.puts( nl "The 1 former student record was left in memory." nl );
else
stdout.put( nl "The ", cnt, " former student records were left in memory." nl );
//mov( 0, count );// done above ... So old count of student records will NOT be updated.
jmp endit;
endif;
endif;
   
// If the program reaches here ... then ...

while( !fileio.eof( fp ) ) do
mem.alloc(@size(Student));
mov( eax, ecx );
fileio.get( fp, tmpStr  );
conv.strToi32( tmpStr, 0 );
mov( eax, (type Student[ecx]).id );
fileio.a_gets( fp );
mov( eax, (type Student[ecx]).sname );
for( mov( 0, edx ); edx < max; inc( edx ) ) do
mov( edx, ebx );
shl( 2, ebx ); // ebx*4
fileio.get( fp, tmpStr );
conv.strToi32( tmpStr, 0 );
mov( eax, (type Student[ecx]).marks[ebx] );
  endfor;
       
//insert( ecx );
append( ecx ); // ... since file already in order ... sorted by ID
       
inc( count );
endwhile;
fileio.close( fp );
   
if( count==1 ) then
stdout.puts( nl "1 record was read into memory from the disk file." nl );
else
stdout.put( nl, count, " records were read into memory from the disk file." nl );
endif;

 endit:
str.free( tmpStr );     
if( count>0 ) then mov( false, fileFlag ); endif;
mov( count, eax ); // return count of student records found in the file.
 
pop( edx ); pop( ecx ); pop( ebx );
end readFile;

// sort an array of pointers
// sort in ascending order of the names pointed to ...
procedure bubbleSort( a:dword );
var
noswap: boolean;
begin bubbleSort;
push( eax ); push( ebx ); push( ecx ); push( edx ); push( esi ); push( edi );

mov( a, ebx );
// repeat
repeatTop:

mov( true, noswap );

//for( mov( 1, edx ); edx < cnt; inc( edx ) ) do
mov( 1, edx );
beginLoop:

mov( edx, ecx );
sub( 1, ecx );
mov( [ebx+ecx*4], esi ); // get 1st pointer into esi
mov( [ebx+edx*4], edi ); // get 2nd pointer into edi
if( str.gt( (type Student[esi]).sname, (type Student[edi]).sname ) ) then
mov( esi, [ebx+edx*4] );
mov( edi, [ebx+ecx*4] );
mov( false, noswap );
endif;

//endfor;
inc( edx );
cmp( edx, cnt );
jb beginLoop;

// until( noSwap );
cmp( noswap, true );
jne repeatTop;

pop( edi ); pop( esi ); pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end bubbleSort;

#macro showByName( a );
if(  cnt==0 ) then
stdout.puts( nl "No records in memory at present." nl );
else
push( ecx ); push( edx );
for( mov( 0, ecx ); ecx<cnt; inc( ecx ) ) do
mov( a, edx );
mov( [edx+ecx*4], edx ); // get next pointer into edx
view( edx );
stdout.newln();
endfor;
stdout.newln();
pop( edx ); pop( ecx );
endif;   
#endmacro

procedure showAllByName;
var
ary: dword;
begin showAllByName;
push( eax ); push( ecx );

// create an array of pointers to hold pointers in list
mov( cnt, eax );
shl( 2, eax ); // eax*4
mem.alloc( eax );
mov( eax, ary );

// copy list pointers to ary
mov( pHead, ecx );
while( ecx != NULL ) do
mov( ecx, [eax] ); // copy pointer into array
add( 4 , eax ); // get next ary address
mov( (type Student[ecx]).next, ecx ); // get next pointer in list
endwhile;
//showByName( ary ); // cnt records

// sort ary of pointers, with cnt linked names, by name
bubbleSort( ary ); // cnt records

// show cnt records sorted by name, used sorted array of pointers
showByName( ary ); // cnt records

mem.free( ary );
pop( ecx ); pop( eax );
end showAllByName;


procedure menu; @returns( "al" );
begin menu;
stdout.puts( header1 );
if( cnt == 1 ) then stdout.puts( "Presently there is 1 record in RAM." nl nl );
else stdout.put( "Presently there are ", cnt, " records.", nl, nl );
endif;
stdout.puts( header2 );
stdout.puts( nl "Please enter your selection: ");
stdin.flushInput();
stdin.getc();
end menu;



begin studentList_test;

mov( readFile(), cnt );
showAll();
forever

menu();
if ( al=='1' || al=='i' || al=='I' ) then init();
elseif ( al=='2' || al=='a' || al=='A' ) then addInsertNew();
elseif ( al=='3' || al=='d' || al=='D' ) then viewDel();
elseif ( al=='4' || al=='e' || al=='E' ) then viewEdit();
elseif ( al=='5' || al=='s' || al=='S' ) then showAll();
elseif ( al=='6' || al=='n' || al=='N' ) then showAllByName();
elseif ( al=='7' || al=='f' || al=='f' ) then
if( fileFlag ) then
mov( writeFile(), cnt);
stdout.put( cnt, " records were filed." nl );
endif;
elseif ( al=='8' || al=='r' || al=='R' ) then
// If any were read ... will update Global variable pHead
if( readFile() > 0 ) then
mov( eax, cnt ); // update Global cnt
endif;
elseif  ( al=='9' || al=='x' || al=='X' ) then
if( fileFlag ) then
mov( writeFile(), cnt );
stdout.put( cnt, " records were filed." nl );
endif;
break;
else stdout.puts( nl "Not implemented yet ..." nl);
endif;

endfor;

end studentList_test;
Title: Re: New thread especially for students of C and C++
Post by: David on January 15, 2009, 02:37:59 AM
Just a simple Student shell in C++ ... to demo a vector of struct using push_back ...

Code: [Select]
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;

struct Data
{
    string name;
    string id;
    int balance; // in cents maybe ?
};

bool more();

int main()
{
    // create a empty vector like this
    vector< Data > myBank;

    // now using push_back ... read in the file

    Data tmp; // create a tmp entry
    do
    {
        cout << "Name    : ";
        getline( cin, tmp.name );
        cout << "ID      : ";
        getline( cin, tmp.id );
        cout << "Balance : ";
        cin >> tmp.balance;
        if( !cin.good() )
        {
            cin.clear(); // clear error flag(s) ...
            cin.sync(); // flush cin stream ...
            continue; // right now ... from the condition (at the bottom) in the 'do loop' ... (so skips push_back)
        }
        cin.sync();
        myBank.push_back( tmp ); // now ... enlarge the vector
    }while( more() );
   
    // show elements in vector  ...
    for (unsigned i=0; i<myBank.size(); ++i )
    {
        cout << setw(4)<< i+1 << " "
             << left << setw(20) << myBank[i].name << " "
             << setw(8) << myBank[i].id << " "
             << right << setw(8) << myBank[i].balance
             << endl;
    }
    //system("pause");
    cout << "\nPress 'Enter' to continue ..." << flush; cin.sync(); cin.get();  
}

bool more()
{
    cout << "More (y/n) ? ";
    int reply = cin.get();
    cin.sync(); // flush cin stream ...
    return !(reply=='n' || reply=='N');
}


Or even simpler, an array of struct ...

Code: [Select]
// This program should use an array of at least 20 structures.
// It should let the user enter data into the array, change
// the contents of any element (*yet to be done* ) and display
// all the data stored in the array ... (for starters.)

// You could do something like this ... ( as a shell to get started )

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

const int MAX = 20;

struct Data
{
    string name;
    string id;
    int balance; // in cents
};

bool more();

int main()
{
    // create a empty vector like this
    Data myBank[MAX];
    int i=0;
    do
    {
        cout << "Name    : ";
        getline( cin, myBank[i].name );
        cout << "ID      : ";
        getline( cin, myBank[i].id );
        cout << "Balance : ";
        cin >> myBank[i].balance;
        if( !cin.good() )
        {
            cin.clear();
            cin.sync();
            continue;
        }
        cin.sync();
        ++i;
       
    }while( more() && i<MAX );
   
    // show elements in vector ...
    for (int j=0; j<i; ++j )
    {
        cout << setw(4)<< j+1 << " "
             << left << setw(20) << myBank[j].name << " "
             << setw(8) << myBank[j].id << " "
             << right << setw(8) << myBank[j].balance
             << endl;
    }
    cout << "\nPress 'Enter' to continue ... " << flush;
    cin.sync();
    cin.get();           
}

bool more()
{
    cout << "More (y/n) ? ";
    int reply = cin.get();
    cin.sync();
    return !(reply=='n' || reply=='N');
}


Now ... really backing up here ... (for C++beginners)

A mini-introduction to arrays ...

Code: [Select]
// A few examples of passing arrays in C++ ... beginning with a fairly simple 1-D array

// Note: In C/C++ 'arrays are always (automatically) passed by reference'
// See what this means below ...

// When passing arrays ... the address to the first element is copied into
// a local copy of that array address ... (the compiler adds code to do this)
// SO ... NO copy of the array, (all the array values), is made by the function.
// Thus ... since the address is being used...  changes made to the array,
// inside the function, are really changes made to the array itself.

// In C++, objects (other than arrays) can be passed by reference by adding
// a '&' before the variable (i.e. after the type) in the function def'n header
// line ... See below for an example function prototype:

// void getInteger( string prompt, int & intToGet );

// then call the function with syntax like this:

// int myLocalInt;  getInteger( "Please enter a number: ", myLocalInt );

// See the '&' that tells the compiler that this is a 'pass by ref' and so to
// add the appropriate code to take care of ... all of that.

// BUT with arrays, you don't add this '&' ...
// since ... 'arrays are always passed by ref' in both C and C++

// Note: in C, 'passing by ref' syntax is NOT available.
// In C, the addresses may be passed, but you have to code all this your-self,
// taking/passing the address when you call the function and taking the value
// at that address inside the function when you want the value (not the address)

// In C (and C++) pointer passing works like this:

// /* this function returns a good positive int ... or a neg value if error ... */
// void getPosInteger( char prompt[], int * intToGet )
// {
//     /* here we de-reference the address and deal with the value there ... */
//     *intToGet = -1; /* set to -1 sentinel value here */
//     printf("%s", prompt);
//     /* note that here, intToGet is already an address, as expected by scanf */
//     scanf("%d", intToGet);
// }

// Then calling that ... works like this ... (in C or in C++)

// int myLocalInt;
// ...
// getInteger( "Please enter a number: ", &myLocalInt );

// NOTE the  ...  & ... (i.e. take the address of the variable) in the call!


// Ok ... now to the C++ demo re. arrays...

// Passing 1-D arrays is easy ...
#include <iostream>
using namespace std;


// Passing an array of int ...
// Reads ... 'a' is an array of int
void showAryInts( int a[], int size )
{
    for( int i = 0; i < size; ++i )
        cout << a[i] << " ";
    cout << endl;
}


// Just a little more care to ...
// pass an array of pointers ...to char
// (This array is called 'a ragged array' ... because
//  each C string there may be a different length.
//  Recall the C strings are really arrays of char ...
//  (or pointers to char) that ALWAYS have a '\0' char
// at the end of their sequence of char's in memory

// Reads ...  'a' is an array of pointers to char
void showAryCStrings( char *a[], int size )
{
    for( int i = 0; i < size; ++i )
        cout << a[i] << endl;
}


const int SECOND_DIM = 3;

// Passing a 2D array ...
// NOTE: We MUST specify the 2nd dimension here.
void show2DAryInts( int a[][SECOND_DIM], int firstD  )
{
    for( int i = 0; i < firstD; ++i )
    {
        for( int j = 0; j < SECOND_DIM; ++j )
            cout << a[i][j] << " ";
        cout << endl;
    }
}


int main()
{
    // delare AND initialize some arrays ...

    // a 1-D array ... of 'int' ...
    int testInts[ ] = { 1, 2, -6, 7, 99, 11, 33, 0, 9, 10 } ;

    // a 'ragged array' of C strings (is really an array of pointers to char)
    char *testChars[] = { "Apple", "Banana", "Cherry" };
   
    // 2-D arrays of 'int'
    // The compiler needs the 2nd-dimension at compile time ...
    // so it knows THEN ... how many elements are in each column ... so that
    // it can then count ... how many rows it needs
    int testMatrix[][SECOND_DIM] = { {1,2,3}, {4,5,6} };
    int testMatrix2[][SECOND_DIM] = { 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5 };

    cout << "Note the C++ syntax in this program regarding ...\n"
         << "\nShowing a simple 1-D array of int's ...\n";
    showAryInts( testInts, sizeof(testInts)/sizeof(int) );
   
    cout << "\nShowing a simple 1-D (ragged) "
         << "array of pointers to char's ...\n";
    showAryCStrings( testChars, sizeof(testChars)/sizeof(testChars[0]) );

    cout << "\nShowing 2-D arrays of int's ...\n";
    show2DAryInts( testMatrix, sizeof(testMatrix)/sizeof(testMatrix[0]) );
    cout << endl;
    show2DAryInts( testMatrix2, sizeof(testMatrix2)/sizeof(testMatrix2[0]) );

    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
}
Title: Re: New thread especially for students of C and C++
Post by: David on January 15, 2009, 02:50:47 AM
Time out for a little reflection on computers and computing ...

i.e. standing back a little to SEE the BIG PICTURE ...

NOTE WELL the process, putting little things together ... until we end up with some amazingly big and powerful packages.  But this one thing ... should be obvious ... but it is NOT so obvious THESE dumbed down DAYS ...

NOW ... Here it is ...

It took somebody ... (many somebodies to put it all <i.e. the first computer> together) ...

And so ... In the beginning God created the heavens and the earth ... it did NOT just all happen by itself!

Even a beginner can see that it took a lot of intelligence, work, material ... to put together the first CPU ... and it took materials, energy, planning, work, design, purpose, language, coding, decoding ...etc ...to get it to do anything purposeful ... get the drift ... And look at all the work, planning, thinking ... since then to get to OOP ... and INTEL and LINUX !!!


Shalom shalom,


David

P.S.  Take a further look at Chapter 17 ... (see next link to link to 'Chapter 17')

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

and the program there.  It is just a little French and English intro to the Good News of our Creator ... and soon coming King of Kings. Please see these links also: 

http://sites.google.com/site/andeveryeyeshallseehim/home
http://sites.google.com/site/andeveryeyeshallseehim/home/he-comes
http://sites.google.com/site/andeveryeyeshallseehim/he-comes-part1

Yes ... it may NOT be that much longer until this most awesome event transpires:

    * Behold, he cometh with clouds;
    * and every eye shall see him,
    * and they also which pierced him:
    * and all kindreds of the earth shall wail because of him.
    * Even so,
    * Amen.
    * (Revelation 1:7)
Title: Re: New thread especially for students of C and C++
Post by: admin on January 20, 2009, 09:16:23 AM
Thanks David, Go on...
Title: Re: New thread especially for students of C and C++
Post by: David on March 13, 2009, 09:26:44 AM
A little demo of a C++ list container to hold Student records ... (makes it easy to delete a record)

Code update/moved to appear later ... (near the end at present ... click on the next link to jump to there right now:)

http://developers-heaven.net/forum/index.php/topic,106.msg272.html#msg272



Click on the small 2 in page 2 (in the lower left corner) to continue on ...
Title: Re: New thread especially for students of C and C++
Post by: David on March 13, 2009, 09:50:02 AM
Now backing up some, for beginners in C++ ...

Here are some steps to develop a C++ program using 2-D arrays ... (as a spread sheet) ... that leads up to an OOP version 'class Stock' ... (that attempts to show that C++ classes are a safe way ... instead of using global variables... to get simple function calls.)

This program asks for input ... and then displays all the inventory of shirts.

There are shirts for 6 teams ... and each team has 4 sizes ... small, medium, large and extra-large.  The program shows the totals for each size, for each team, and the grand total of shirts in stock.

Here is a 1st step working shell to get started ...

Code: [Select]
#include <iostream>

using namespace std;

const int NUM_TEAMS = 6; // reset when all debuggin down ...keep small for now
const int NUM_SIZES = 4;
const char header[] = "This program will prompt for the number of shirts "
                      "in stock\n";

// using a global array for simplicity of function calls (for now ...)
int stock[NUM_SIZES][NUM_TEAMS]; // here ... a 4x6 array to keep stock

// function for debugging ...
void show()
{
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
            cout << stock[i][j] << "  ";
        cout << endl;
    }
}

int main()
{
    cout << header << endl << endl;

    char *shirt_sizes[] = { "small", "medium", "large", "extra-large" };
   
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            cout << "Size " << shirt_sizes[i] << ", enter the number of "
                 << "t-shirts for team " << j+1 << ": " << flush;
            cin >> stock[i][j];
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
    show();

    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.sync();
    cin.get();
}

step 2 ... (see comments in program)

Code: [Select]
// part of the formatted output is added in this 2nd step ...
// (we still need to show the sum of each column in our 'output')

// Note: here, we are no-longer using a global array for 'stock' ...
// since we are doing all our work inside main (scope)

#include <iostream>
#include <iomanip>

using namespace std;

const int NUM_TEAMS = 6; // reset when all debuggin down ... keep small for now
const int NUM_SIZES = 4;
const char header[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

int main()
{
    cout << header << endl << endl;
    int stock[NUM_SIZES][NUM_TEAMS]; // here ... a 4x6 array
    char *shirt_sizes[] = { "Small", "Medium", "Large", "X-large" };
   
    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            cout << "Size " << shirt_sizes[i] << ", enter the number of "
                 << "t-shirts for team " << j+1 << ": " << flush;
            cin >> stock[i][j];
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
    // show report ...
    int w1=10, w2=6;
    cout << right << setw(21) << " " << "INVENTORY REPORT ...\n\n"
         << right << setw(21) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl << endl;
   
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << shirt_sizes[i];
        int tot = 0;
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
            tot += stock[i][j];
        }
        cout << setw(w2) << right << tot << endl;
        grand_tot += tot;
    }
   
    cout << "\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl;
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.sync();
    cin.get();
}

Step 3 ...

Code: [Select]
// Here ... we add the function to sum the columns 'sumCols( colNum )'
// and we add this new output when we show the report ...

#include <iostream>
#include <iomanip>

using namespace std;

const int NUM_TEAMS = 6; // reset when all debuggin down ... keep small for now
const int NUM_SIZES = 4;
const char header[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// placed in global scope (to avoid having to pass a 2-D array to functions)
int stock[NUM_SIZES][NUM_TEAMS]; // here ... a 4x6 array

int sumCols( int colNum )
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
    {
        sum += stock[row][colNum];
    }
    return sum;
}



int main()
{
    cout << header << endl << endl;

    char *shirt_sizes[] = { "Small", "Medium", "Large", "X-large" };
   
    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            cout << "Size " << shirt_sizes[i] << ", enter the number of "
                 << "t-shirts for team " << j+1 << ": " << flush;
            cin >> stock[i][j];
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
    // show report ...
    int w1=10, w2=6;
    cout << right << setw(21) << " " << "INVENTORY REPORT ...\n\n"
         << right << setw(21) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl << endl;
   
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << shirt_sizes[i];
        int tot = 0;
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
            tot += stock[i][j];
        }
        cout << setw(w2) << right << tot << endl;
        grand_tot += tot;
    }
   
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        cout << setw(w2) << sumCols(j);
    }
   
    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl;
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.sync();
    cin.get();
}

Step 4a ...

Code: [Select]
// This (potentially) final version, shows a syntax to pass 2-D arrays ...
// (so array is NOT in global scope here ... ( it has 'main' scope )

// NOTE: the attempt here to use 'automatic-self-formatting' ... if the
// program main data structure, a 2D array, were to have its dim's updated
// for a new 'store' ...

// NOTE: the use of more fully descriptive names for all variables, as an aid to
// 'self-documentation' ... to also readily SEE the the LOGIC and the top-down
// FLOW of the program ...(You do not have to add/read EXTRA comments, IF the
// variable names are SELF-EXPLAINING ...)

// SINCE ... we are taking in so many integers from the keyboard ...
// We REALLY SHOULD BE validating each number ... so we won't crash ...
// especially ... after entering almost all the data .. and then, to have to
// enter it all perfectly, from the top, AGAIN! ... So, done here to show how.

// Also ... moved 'shirt_sizes' array into global scope,
// (as it really should be there as a const array of C strings),
// but also to make for easy calling from inside the 'getInteger(..)' function,
// as well as calling it from 'main' function.

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

// NOTE: the convention of *consistent use* of ALL_CAPS for GLOBAL_CONSTANTS ...
// so ... each time you SEE_ALL_CAPS in the program ...
// you AUTOMATICALLY_KNOW that  ... that is a GLOBAL_CONST_VAR
const int NUM_SIZES = 4;
const int NUM_TEAMS = 6; // reset when debugging done (keep small for testing)
const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
const char *SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// to sum any col in a 2-D array ... the col# is passed in, the sum is returned
int sumCols( int array[NUM_SIZES][NUM_TEAMS], int colNumber )
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
    {
        sum += array[row][colNumber];
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }
       
        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }
   
        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}



int main()
{
    // recall ... 'seeing' all_caps implies a var like HEADER is a global const
    cout << HEADER << endl << endl;

    // Ok ... HERE is our 'MAIN STORE' of stuff ...
    // using 'const' variables the COMPILER knows their value at COMPILE TIME
    // So this array gets DIM'ED then ... (i.e at COMPILE TIME)
    int stock[NUM_SIZES][NUM_TEAMS]; // ... as a 4x6 array of int's

    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
    // show report ...
    int w1=10, w2=6, w3=21;
    // On each line below ... first space over ... w3-6, and w3 spaces ...
    // Need to 'play with spacing' to get it 'just right' for each program,
    // (especially if the number of col's in an array is variable ... NOT, as
    //  in the case here.  But if the global const (and array size) were to be
    // adjusted, then this 'play with spacing' would need to also be re-done.)
    // It is sometimes possible to use 'calulations' in a running program
    // to auto-matically ... space and format appropriately ... if data
    // structure sizes are 'updated' in a new version of a program ...
    // The rudiments, of this, were attemted here, via using w1, w2, w3 var's
    // which can easily change all the relevant formatting they control ...
    // by just changing the one parameter value ...
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;
   
    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
   
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        int tot = 0;
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
            tot += stock[i][j];
        }
        cout << setw(w2) << right << tot << endl;
        grand_tot += tot;
    }
   
    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
   
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function sumCols( aryName, colNumber )
        cout << setw(w2) << sumCols( stock, j);
    }
   
    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl

         << "\nPress 'Enter' to exit ... " << flush;
    //cin.sync(); // not needed here ... since we 'flushed' in each 'getInteger'
    cin.get();
}

... and 4b

Code: [Select]
// ** Just to show we can leave off the first Dimension **
// ** (in the header line), when passing 2-D arrays **
// Just one functional change here ... See the header line 20 lines below
// with this comment: ** ..... see above ..... **

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
const char *SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// to sum any col in a 2-D array ... the col# is passed in, the sum is returned
int sumCols( int array[][NUM_TEAMS], int col ) // ** ..... see above ..... **
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
    {
        sum += array[row][col];
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }
       
        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }
   
        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}



int main()
{
    // recall ... 'seeing' all_caps implies a var like HEADER is a global const
    cout << HEADER << endl << endl;

    // Ok ... HERE is our 'MAIN STORE' of stuff ...
    // using 'const' variables the COMPILER knows their value at COMPILE TIME
    // So this array gets DIM'ED then ... (i.e at COMPILE TIME)
    int stock[NUM_SIZES][NUM_TEAMS]; // ... as a 4x6 array of int's

    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
    // show report ...
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;
   
    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
   
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        int tot = 0;
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
            tot += stock[i][j];
        }
        cout << setw(w2) << right << tot << endl;
        grand_tot += tot;
    }
   
    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function sumCols( aryName, colNumber )
        cout << setw(w2) << sumCols( stock, j);
    }
   
    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl

         << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
}

Continued on the next page ...
Title: Re: New thread especially for students of C and C++
Post by: David on March 13, 2009, 10:17:11 AM
And 4typedef ...

Code: [Select]
// This demo's 'the ease' available in C/C++ by using typedef ...
// see the 4 places in this progran marked // *** // below ...

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6; // reset when all debugging done (keep small for testing)

// Now .. Grid2D specifies a 4x6 2-D array of int's
typedef int Grid2D[NUM_SIZES][NUM_TEAMS]; // *** // (0)

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
typedef char* CString; // *** // (1)
const CString SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// to sum any col in a 2-D array ... the col# is passed in, the sum is returned
int sumCols( Grid2D grid, int col ) // *** // (2)
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
    {
        sum += grid[row][col];
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }
       
        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }
   
        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}



int main()
{
    // recall ... 'seeing' all_caps implies a var like HEADER is a global const
    cout << HEADER << endl << endl;

    // Ok ... HERE is our 'MAIN STORE' of stuff ...
    // using 'const' variables the COMPILER knows their value at COMPILE TIME
    // So this array gets DIM'ED then ... (i.e at COMPILE TIME)

    Grid2D stock; // ... as a 4x6 array of int's // ** // (3)

    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
    // show report ...
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;
   
    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
   
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        int tot = 0;
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
            tot += stock[i][j];
        }
        cout << setw(w2) << right << tot << endl;
        grand_tot += tot;
    }
   
    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function sumCols( aryName, colNumber )
        cout << setw(w2) << sumCols( stock, j);
    }
   
    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl

         << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
}

Step 5a ...

Code: [Select]
// A demo using a total functional approach  ... (for demo purposes)
// But sometimes, (as demo'd here), using functions is NOT really so efficent.

// Note: here, in our function headers, we elect to provide ONLY the minimum
// info necessary to the compiler, at compile time, when passing a 2-D array ...
// i.e. we leave the first 2-D array dimension empty ...
// ( as per the new header shown below ... )

// int sumCol( int array[][NUM_TEAMS], int numRows, int col )

// We can now pass in the number of rows at *RUN time* ...
// This function is now more portable with one more run time variable that
// can be altered by the running program.

// If we consider a 2-D array as a 'spread-sheet' ...
// One might like functions to ...
// 1. sum down a col (which function we did in the last program)
// 2. sum across a row (which we did in the previous versions during output)
// 3. sum all ... (which we did during output in the previous versions)

// Here we will demo an implementation of 2. ...
// (and remove the code that did this before ... during output.)

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;
const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
const char *SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };


// to sum any col in a 2-D array ... the col# is passed in, the sum is returned
int sumCol( int array[][NUM_TEAMS], int numRows, int colNumber )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
    {
        sum += array[row][colNumber];
    }
    return sum;
}


// added here in 'xxxStock5a.cpp' ...
// to sum any row in a 2-D array ... the ROW ITSELF is passed in ... and
// the sum is returned ...
int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}


// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }
       
        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }
   
        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}



int main()
{
    // recall ... 'seeing' all_caps implies a var like HEADER is a global const
    cout << HEADER << endl << endl;

    // Ok ... HERE is our 'MAIN STORE' of stuff ...
    // using 'const' variables the COMPILER knows their value at COMPILE TIME
    // So this array gets DIM'ED then ... (i.e at COMPILE TIME)
    int stock[NUM_SIZES][NUM_TEAMS]; // ... as a 4x6 array of int's

     ///////////////////////////////////////////////////////////////////////////
    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
   
     ///////////////////////////////////////////////////////////////////////////
    // show report ...
    int w1=10, w2=6, w3=21;
    // On each line below ... first space over ... w3-6, and w3 spaces ...
    // Need to 'play with spacing' to get it 'just right' for each program,
    // (especially if the number of col's in an array is variable ... NOT, as
    //  in the case here.  But if the global const (and array size) were to be
    // adjusted, then this 'play with spacing' would need to also be re-done.)
    // It is sometimes possible to use 'calulations' in a running program
    // to auto-matically ... space and format appropriately ... if data
    // structure sizes are 'updated' in a new version of a program ...
    // The rudiments, of this, were attemted here, via using w1, w2, w3 var's
    // which can easily change all the relevant formatting they control ...
    // by just changing the one parameter value ...
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;
   
    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
   
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        int row_tot = sumRow( stock[i], NUM_TEAMS ); // now call function sumRow(..)
        cout << setw(w2) << right << row_tot << endl;
        grand_tot += row_tot;
    }
   
    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function sumCols( aryName, numRows, colNumber )
        cout << setw(w2) << sumCol( stock, NUM_SIZES, j);
    }
   
    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl

         << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
}

Step 5b ...

Code: [Select]
// A total functional approach ...(but not so efficent for this simple program)

// Treating a 2-D array as a 'spread-sheet' ...

// Recall ... In 'xxxStock5a.cpp' we added the function to sum a row,
// where we passed THAT ROW IT-SELF as a 1-D array to be summed ...

// Close attention may be needed here to grasp this 'simple concept' ...
// In order to sum a column ... can we use a function that gets the column value
// in a particuler row ?
// Yes we can ... That change is added here ... (just to demo a concept).
// We need two new functions ... one is a slightly changed sumCol(...)


#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;
const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
const char *SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };


// added here in 'xxxStock5b.cpp' ... just for 'concept demo' purposes ...
// returns the value in a 1-D array at index = i
int getVal_i( int ary[], int i )
{
    return ary[i];
}


// changed here in 'xxxStock5b.cpp' ... just for 'concept demo' purposes ...
// now calls a function that returns the value of a 1-D array at index specified
// sums any col in a 2-D array ... the col# is passed in, the sum is returned
int sumCol( int array[][NUM_TEAMS], int numRows, int colNumber )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
        sum += getVal_i(array[row], colNumber);

    return sum;
}


// added in 'xxxStock5a.cpp' ...
// to sum any row in a 2-D array ... the ROW itself is passed in ... and
// the sum is returned ...
int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}


// uses utility global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }
        
        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }
    
        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}



int main()
{
    // recall ... 'seeing' all_caps implies a var like HEADER is a global const
    cout << HEADER << endl << endl;

    // Ok ... HERE is our 'MAIN STORE' of stuff ...
    // using 'const' variables the COMPILER knows their value at COMPILE TIME
    // So this array gets DIM'ED then ... (i.e at COMPILE TIME)
    int stock[NUM_SIZES][NUM_TEAMS]; // ... as a 4x6 array of int's

     ///////////////////////////////////////////////////////////////////////////
    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
    
     ///////////////////////////////////////////////////////////////////////////
    // show report ...
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";
        
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;
    
    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    
    int grand_tot = 0;
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        // now call function sumRow(..) ...
        int row_tot = sumRow( stock[i], NUM_TEAMS );
        cout << setw(w2) << right << row_tot << endl;
        grand_tot += row_tot;
    }
    
    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function sumCol( aryName, numRows, colNumber )
        cout << setw(w2) << sumCol( stock, NUM_SIZES, j);
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')<< grand_tot << endl

         << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
}

Continued on next page ...
Title: Re: New thread especially for students of C and C++
Post by: David on March 13, 2009, 10:24:08 AM
Step 5c ...

Code: [Select]
// A total functional approach ... (but not so efficent for this simple program)

// Treating a 2-D array as a 'spread-sheet' ...

// Here in 'xxxStock5c.cpp' we will implement the 3rd new function ...
// 'sumAll' the values in our 2-D grid ... a common spread-sheet function also.

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;
const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
const char *SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };


// added in 'xxxStock5b.cpp' ... just for 'concept demo' purposes ...
// returns the value in a 1-D array at index = i
int getVal_i( int ary[], int i )
{
    return ary[i];
}


// changed in 'xxxStock5b.cpp' ... just for 'concept demo' purposes ...
// now calls a function that returns the value of a 1-D array at index specified
// sums any col in a 2-D array ... the col# is passed in, the sum is returned
int sumCol( int array[][NUM_TEAMS], int numRows, int col )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
        sum += getVal_i(array[row], col);
        // just a demo to ?help? show that this is the same thing as
        // array[row][col]
        // and that here ... 2-D arrays are just 'an array of arrays' object

    return sum;
}


// added in 'xxxStock5a.cpp' ...
// to sum any row in a 2-D array ... the ROW itself is passed in ... and
// the sum is return ...
int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}


// HERE we add, in 'xxxStock5c.cpp' ... sumAll( ... )
// and the grand sum is returned
int sumAll( int ary[][NUM_TEAMS], int numRows, int numCols )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
    {
        sum += sumRow( ary[row], numCols );
    }
    return sum;
}


// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }
       
        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }
   
        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}



int main()
{

     ///////////////////////////////////////////////////////////////////////////
    // show header ...
    cout << HEADER << endl << endl;


     ///////////////////////////////////////////////////////////////////////////
    // our main 'store' to keep all stock ...
    int stock[NUM_SIZES][NUM_TEAMS]; // ... as a 4x6 array of int's


     ///////////////////////////////////////////////////////////////////////////
    // take-in info ...
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }


     ///////////////////////////////////////////////////////////////////////////
    // show report ...
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";
         
    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;
   
    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
   
    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( stock[i], NUM_TEAMS ) << endl;
    }
   
    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( stock, NUM_SIZES, j );
    }
   
    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll(stock, NUM_SIZES, NUM_TEAMS ) << endl

         << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
}

Step 5d ...

Code: [Select]
// demo of inventory being stored in a 2-D array ...
// a total functional approach ... (potentially final version)

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
const char *SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// function prototypes ...

int getVal_i( int ary[], int i );
int sumCol( int ary[][NUM_TEAMS], int numRows, int col );
int sumRow( int ary[], int numCols );
int sumAll( int ary[][NUM_TEAMS], int numRows, int numCols );
int getInteger( int min, int i, int j );
void updateStock( int ary[][NUM_TEAMS], int numSizes, int numTeams );
void displayAllStockAndTotals( int ary[][NUM_TEAMS], int numSizes, int numTeams );



int main() // ********************* main begin ****************************** //
{
    // show header ...
    cout << HEADER << endl << endl;

    // reserve space for the main 'store' to keep all the stock ...
    int stock[NUM_SIZES][NUM_TEAMS]; // ... as a 4x6 array of int's

    // update stock ...
    updateStock( stock, NUM_SIZES, NUM_TEAMS );

    // show report ...
    displayAllStockAndTotals( stock, NUM_SIZES, NUM_TEAMS  );
   
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
} // ******************************* main ends ****************************** //


// function definitions ...

int getVal_i( int ary[], int i )
{
    return ary[i];
}

int sumCol( int ary[][NUM_TEAMS], int numRows, int col )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
        sum += getVal_i(ary[row], col);

    return sum;
}

int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}

int sumAll( int ary[][NUM_TEAMS], int numRows, int numCols )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
    {
        sum += sumRow( ary[row], numCols );
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }

        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }

        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}

// take-in info ...
void updateStock( int ary[][NUM_TEAMS], int numSizes, int numTeams )
{
    for( int i=0; i<numSizes; ++i)
    {
        for( int j=0; j<numTeams; ++j)
        {
            ary[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
}

// display all in stock and all totals ...
void displayAllStockAndTotals( int ary[][NUM_TEAMS], int numSizes, int numTeams )
{
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";

    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < numTeams; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;

    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');

    for( int i = 0; i < numSizes; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < numTeams; ++j )
        {
            cout << right << setw(w2) << ary[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( ary[i], numTeams ) << endl;
    }

    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < numTeams; ++j )
    {
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( ary, numSizes, j );
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll(ary, numSizes, numTeams ) << endl;
}

Step 5dtypedef

Code: [Select]
// This demo helps to show the 'array of array' stucture of 2-D arrays,
// (with the help of 'typedef')

// A demo of inventory being stored in a 2-D array ... using functions
// Here: a store stocks 4 sizes of shirts for each of 6 different team designs

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;

// AryTeams NOW specifies a type that is:
// an array of int's with NUM_TEAM elements
typedef int AryTeams[NUM_TEAMS];

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
typedef char* CString;
const CString SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// function prototypes ...

int sumCol( AryTeams ary[], int numRows, int col );
int sumRow( int ary[], int numCols );
int sumAll( AryTeams[], int numRows );
int getInteger( int min, int i, int j );
void takeStock( AryTeams ary[] );
void displayAllStockAndTotals( AryTeams ary[] );



int main() // ********************* main begin ****************************** //
{
    // show header ...
    cout << HEADER << endl << endl;

    // our main 'store' to keep all stock ... (An array of arrays)
    // reserve space for NUM_SIZE elements, each element is an AryTeam
    AryTeams stock[NUM_SIZES];

    // update stock ...
    takeStock( stock );

    // show report ...
    displayAllStockAndTotals( stock );
   
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
} // ******************************* main ends ****************************** //


// function definitions ...

int sumCol( AryTeams ary[], int numRows, int col )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
        sum += ary[row][col];
       
    return sum;
}

int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}

int sumAll( AryTeams ary[], int numRows  )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
    {
        sum += sumRow( ary[row], NUM_TEAMS );
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }

        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }

        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}

void takeStock( AryTeams ary[] )
{
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            ary[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
}

void displayAllStockAndTotals( AryTeams ary[] )
{
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";

    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;

    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');

    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << ary[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( ary[i], NUM_TEAMS ) << endl;
    }

    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( ary, NUM_SIZES, j );
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll( ary, NUM_SIZES ) << endl;
}
Title: Re: New thread especially for students of C and C++
Post by: David on March 13, 2009, 10:45:51 AM
Step 5dGlobalArray ...

Code: [Select]
// This demo helps to show the simpilicty that may be obtained sometimes by
// using global variables ...

// A demo of inventory being stored in a 2-D array ... using functions
// Here: a store stocks 4 sizes of shirts for each of 6 different team designs

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;

// AryTeams NOW specifies a type that is:
// an array of int's with NUM_TEAM elements
typedef int AryTeams[NUM_TEAMS];

// our main 'store' to keep all stock ... (An array of arrays)
// reserve space for NUM_SIZE elements, each element is an AryTeam
AryTeams stock[NUM_SIZES]; // in Global Scope to make for simple functiom calls

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
typedef char* CString;
const CString SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// function prototypes ...

int sumCol( AryTeams ary[], int numRows, int col );
int sumRow( int ary[], int numCols );
int sumAll( AryTeams ary[], int numRows );
int getInteger( int min, int i, int j );
void takeStock();
void displayAllStockAndTotals();



int main() // ********************* main begin ****************************** //
{
    // show header ...
    cout << HEADER << endl << endl;

    // update stock ...
    takeStock();

    // show report ...
    displayAllStockAndTotals();
   
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
} // ******************************* main ends ****************************** //


// function definitions ...

int sumCol( AryTeams ary[], int numRows, int col )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
        sum += ary[row][col];
       
    return sum;
}

int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}

int sumAll( AryTeams ary[], int numRows  )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
    {
        sum += sumRow( ary[row], NUM_TEAMS );
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }

        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }

        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}

void takeStock()
{
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
}

void displayAllStockAndTotals()
{
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";

    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;

    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');

    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( stock[i], NUM_TEAMS ) << endl;
    }

    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( stock, NUM_SIZES, j );
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll( stock, NUM_SIZES ) << endl;
}

Step 5dGlobalArraySimpler ...

Code: [Select]
// This demo helps to show the simpilicty that may be obtained sometimes by
// using global variables ...

// A demo of inventory being stored in a 2-D array ... using functions
// Here: a store stocks 4 sizes of shirts for each of 6 different team designs

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;

// AryTeams NOW specifies a type that is:
// an array of int's with NUM_TEAM elements
typedef int AryTeams[NUM_TEAMS];

// our main 'store' to keep all stock ... (An array of arrays)
// reserve space for NUM_SIZE elements, each element is an AryTeam
AryTeams stock[NUM_SIZES]; // in Global Scope to make for simple functiom calls

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";

// a utility ragged-array of C strings ... to aid output
typedef char* CString;
const CString SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };

// function prototypes ...

int sumCol( AryTeams ary[], int numRows, int col );
int sumRow( int ary[], int numCols );
int sumAll( AryTeams ary[], int numRows );
int getInteger( int min, int i, int j );
void takeStock();
void displayAllStockAndTotals();



int main() // ********************* main begin ****************************** //
{
    // show header ...
    cout << HEADER << endl << endl;

    // update stock ...
    takeStock();

    // show report ...
    displayAllStockAndTotals();
   
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();
} // ******************************* main ends ****************************** //


// function definitions ...

int sumCol( AryTeams ary[], int numRows, int col )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
        sum += ary[row][col];
       
    return sum;
}

int sumRow( int ary[], int numCols )
{
    int sum = 0;
    for( int col=0; col<numCols; ++col )
    {
        sum += ary[col];
    }
    return sum;
}

int sumAll( AryTeams ary[], int numRows  )
{
    int sum = 0;
    for( int row=0; row<numRows; ++row )
    {
        sum += sumRow( ary[row], NUM_TEAMS );
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }

        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }

        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}

void takeStock()
{
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
}

void displayAllStockAndTotals()
{
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";

    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;

    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');

    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( stock[i], NUM_TEAMS ) << endl;
    }

    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( stock, NUM_SIZES, j );
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll( stock, NUM_SIZES ) << endl;
}

Step 5 class Stock ...

Code: [Select]
// class Stock

// This demo seeks to show the similarity of the previous program, that used
// global variables, and this one, that demo's (global) 'class Stock' ...

// Inventory of shirts, stored in a 2-D array, is managed using a C++ class
// Here: a store stocks 4 sizes of shirts for each of 6 different team designs

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;
const char * SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };
const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";
class Stock
{
public:
    Stock(){} // default constructor
    void takeStock();
    void displayAllStockAndTotals();
private:
    // our main 'store' to keep all stock ... (An array of arrays)
    int stock[NUM_SIZES][NUM_TEAMS];
    // private member functions ...
    int getInteger( int min, int i, int j );
    int sumCol( int col );
    int sumRow( int ary[] );
    int sumAll();
};


int main() // ********************* main begin ****************************** //
{
    cout << HEADER << endl << endl;     // show header ...
    Stock s200903;                      // instantiate a Stock object ...
    s200903.takeStock();                // update stock ...
    s200903.displayAllStockAndTotals(); // show report ...

    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();

} // ******************************* main ends ****************************** //


// private member function definitions ...

int Stock::sumCol( int col )
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
        sum += stock[row][col];
       
    return sum;
}

int Stock::sumRow( int ary[] )
{
    int sum = 0;
    for( int col=0; col<NUM_TEAMS; ++col )
    {
        sum += ary[col];
    }
    return sum;
}

int Stock::sumAll()
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
    {
        sum += sumRow( stock[row] );
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int Stock::getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }

        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }

        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}

// public member function definitions

void Stock::takeStock()
{
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
}

void Stock::displayAllStockAndTotals()
{
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";

    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;

    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');

    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( stock[i] ) << endl;
    }

    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
   
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( j );
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll() << endl;
}
Title: Re: New thread especially for students of C and C++
Post by: David on March 13, 2009, 11:36:43 AM
Step 5 (last part)

The 'Last Step' ... class Stock ... with a static const member ... (NOTE the syntax for future reference.)

Code: [Select]
// class Stock ... with static member ...

// This demo seeks to show the similarity of the previous program, that used
// global variables, and this one, that demo's (global) 'class Stock' ...

// Inventory of shirts, stored in a 2-D array, is managed using a C++ class
// Here: a store stocks 4 sizes of shirts for each of 6 different team designs

#include <iostream>
#include <iomanip>

using namespace std;

// Globals ...

const char HEADER[] = "This program will prompt for the number of shirts "
                      "in stock\n"
                      "for each of the 4 sizes for each of the 6 teams ...";
const int NUM_SIZES = 4;
const int NUM_TEAMS = 6;

class Stock
{
public:
    Stock(){} // default constructor
    void takeStock();
    void displayAllStockAndTotals();
private:
    // our main 'store' to keep all stock ... (An array of arrays)
    int stock[NUM_SIZES][NUM_TEAMS];
    // private member functions ...
    int getInteger( int min, int i, int j );
    int sumCol( int col );
    int sumRow( int ary[] );
    int sumAll();
    // note the syntax for static const variables ...
    static const char* SHIRT_SIZES[];
};
// static var's MUST be given their inital values 'outside' ...(NOT 'inline')
const char* Stock::SHIRT_SIZES[] = { "Small", "Medium", "Large", "X-large" };


int main() // ********************* main begin ****************************** //
{
    cout << HEADER << endl << endl;     // show header ...
    Stock s200903;                      // instantiate a Stock object ...
    s200903.takeStock();                // update stock ...
    s200903.displayAllStockAndTotals(); // show report ...

    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get();

} // ******************************* main ends ****************************** //


// private member function definitions ...

int Stock::sumCol( int col )
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
        sum += stock[row][col];
       
    return sum;
}

int Stock::sumRow( int ary[] )
{
    int sum = 0;
    for( int col=0; col<NUM_TEAMS; ++col )
    {
        sum += ary[col];
    }
    return sum;
}

int Stock::sumAll()
{
    int sum = 0;
    for( int row=0; row<NUM_SIZES; ++row )
    {
        sum += sumRow( stock[row] );
    }
    return sum;
}

// uses global array SHIRT_SIZES ...
int Stock::getInteger( int min, int i, int j )
{
    for( ;; ) // forever loop ... until return a good value ...
    {
        int tmp;
        cout << "For size < " << setw(7) << SHIRT_SIZES[i] << " >, enter "
             << "the number of t-shirts for team " << j+1 << ": " << flush;
        cin >> tmp;
        if( !cin.good() )
        {
            cin.clear(); // clear error flags ...
            cin.sync(); // flush cin stream ...
            cout << "\nBad data entered ... Integers only please.\n" << flush;
            continue; // right now from the top of the for loop ...
        }

        if( tmp < min )
        {
            cout << "\nNumbers must be greater than " << min << " ...\n" << flush;
            cin.sync(); // flush cin stream ...
            continue; // right now from the top of the for loop ...
        }

        // if we reach here ... an integer was entered as the first
        // non-whitespace char's ... so ... first
        cin.sync(); // 'flush' ....
        return tmp; // return the good number
    }
}

// public member function definitions

void Stock::takeStock()
{
    for( int i=0; i<NUM_SIZES; ++i)
    {
        for( int j=0; j<NUM_TEAMS; ++j)
        {
            stock[i][j] = getInteger( 0, i, j ); // valid numbers 0,1,2, ...
        }
        cout << endl; // to space out each 'size' entry ...
    }
}

void Stock::displayAllStockAndTotals()
{
    int w1=10, w2=6, w3=21;
    cout << right << setw(w3-6) << " " << "*** Shirt Inventory Report ***\n\n"
         << right << setw(w3) << " " << "Team-College-Number\n\n";

    cout << left << setw(w1) << "Size"
         << right;
    for( int i = 0; i < NUM_TEAMS; ++i )
    {
        cout << setw(w2) << i+1;
    }
    cout << setw(w2) << "Total" << endl;

    // now ... print a solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');

    for( int i = 0; i < NUM_SIZES; ++i )
    {
        cout << left << setw(w1) << SHIRT_SIZES[i]; // *all_caps* => 'global const'
        for( int j = 0; j < NUM_TEAMS; ++j )
        {
            cout << right << setw(w2) << stock[i][j];
        }
        // now call call our function to sum this 'ith' row ...
        cout << setw(w2) << right << sumRow( stock[i] ) << endl;
    }

    // now ... print the same (as above) solid line divider ...
    cout << setw(52) << setfill('_') << '_' << endl << setfill(' ');
    cout << endl << left << setw(w1) << "Team total"
         << right;
    for( int j = 0; j < NUM_TEAMS; ++j )
    {
        // now call function to sum the 'jth' col ...
        cout << setw(w2) << sumCol( j );
    }

    cout << "\n\nThe grand total of shirts in stock is "
         << right << setw(14) << setfill('.')
         // now call the function to sum All ...
         << sumAll() << endl;
}
Title: Re: New thread especially for students of C and C++
Post by: David on March 25, 2009, 12:11:01 PM
And while we are at 'class' ... and static (variable) or const static class members ...

Here is a class Time and (extended) class ExtTime that stores the time zones (PST, MST, CST, EST) ... that shows how the extended class ExtTime INHERITS the protected and public members of class Time ...

Code: [Select]
// class Time, class ExtTime, also demos a (const) static class member

// this version 2010-05-23

#include <iostream>
#include <string>
#include <cstdlib> // re. exit

void myAssert( bool condition, char text[] )
{
    if( !condition )
    {
        std::cerr << text <<  "\nPress 'Enter' to exit ... " << std::flush;
        std::string dummy;
        getline( std::cin, dummy );
        exit(1);
    }
}

int getInt( char prompt[], int max );

class Time
{
public:
    Time(int h=0, int m=0);
    void setHour();     //sets hour from keyboard ...
    void setMinute();   //sets minute from keyboard ...
    int getHour() { return hour; }
    int getMinute() { return minute; }
    void incHour();
    void incMinute();
    void printTime();

protected: //these are 'only protected' so that they will be known in 'ExtTime'
    int hour, minute;

    void normalize(); // 'protected' as they are used ONLY BY 'Time' & 'ExeTime'
};

class ExtTime : public Time
{
public:
    ExtTime(int h=0, int m=0, std::string z="EST");
    void setZone(); //sets zone from keyboard ...
    void printTime();
    void printAll4RelativeTimes();

private:
    std::string zone;
    const static std::string myZones[];// = { "EST", "CST", "MST",  "PST" };
};

// Note: static members MUST be initialed OUTSIDE the class header declarations
const std::string ExtTime::myZones[] = { "EST", "CST", "MST",  "PST" };

int main() // * * * * * * * * * * * * MAIN BEGINS * * * * * * * * * * * * * * //
{
    using std::cout;
    using std::endl;
    using std::flush;
    using std::cin;

    cout << "Testing ExtTime ...\n";
    ExtTime tx;
    tx.setHour();
    tx.setMinute();
    tx.setZone();
    tx.printTime(); cout << endl;
    tx.printAll4RelativeTimes(); cout << endl;

    ExtTime tz(123, 759, "CST");
    tz.printTime(); cout << endl;
    tz.printAll4RelativeTimes(); cout << endl;

    cout << "\nTesting Time ...\n";
    Time myTime(13, 59+60*10+1); cout << endl;
    myTime.printTime(); cout << endl;

    myTime.setHour();
    myTime.setMinute();
    int h = myTime.getHour();
    int m = myTime.getMinute();
    cout << "The hour is " << h << " and the minute is " << m << endl;
    myTime.printTime(); cout << endl;

    myTime.incHour();
    myTime.incMinute();
    cout << "The time after adding 1 hour and 1 minute is\n";
    myTime.printTime();

    cout << "\n\nPress 'Enter' to exit ... " << flush;
    cin.sync();
    cin.get();
}// * * * * * * * * * * * * * * * * * MAIN ENDS * * * * * * * * * * * * * * * //


Time::Time(int h, int m) : hour(h), minute(m)
{
    normalize();
}

ExtTime::ExtTime(int h, int m, std::string z) : Time(h, m), zone(z)
{
    normalize();
}

void ExtTime::setZone()
{
    using std::cout;
    using std::endl;
    using std::flush;
    using std::cin;

    for( ;; ) // loop forever ... until 'return' with a good 'zone'
    {
        cout << "Enter zone: " << flush;
        zone = "";
        getline( cin, zone );

        //if( zone=="EST" || zone=="CST" || zone=="MST" || zone=="PST" ) break;
        for( int i = sizeof myZones/sizeof myZones[0] -1; i >=0 ; --i )
        {
            if( zone == myZones[i] ) return; // exit this function right now ...
        }

        // if reach here ... an invalid zone was entered ...

        //cout <<"Valid zones are EST, CST, MST, PST ..." << endl;
        cout <<"Valid zones are ";
        int i;
        for( i = sizeof myZones/sizeof myZones[0] -1; i >0 ; --i )
            cout << myZones[i] << ", ";
        cout << myZones[i] << endl;
    }
}

void Time::setHour()
{
    hour = getInt("Enter the hour", 23);
}

void Time::setMinute()
{
    minute = getInt("Enter the minute", 59);
}

void Time::incHour()
{
    ++hour;
    normalize();
}

void Time::incMinute()
{
    ++minute;
    normalize();
}

void Time::printTime()
{
    using std::cout;
    cout << "The time is ";
    if(hour<10) cout << "0";
    cout << hour << ":";
    if(minute < 10) cout << "0";
    cout << minute;
}

void ExtTime::printTime()
{
    Time::printTime();
    std::cout << " " << zone << std::endl;
}

void ExtTime::printAll4RelativeTimes()
{
     // Note: the presumption here is that the ZONES are already validated on
     // input ... and are ONE of the known zones in the following array 'myZones'
    int baseZoneIndex;
    //std::string myZones[] = { "EST", "CST", "MST",  "PST" };
    for( int i = sizeof myZones/sizeof myZones[0] -1; i >=0 ; --i )
        if( zone == myZones[i] )
        {
            baseZoneIndex = i;
            break;
        }
    // Ok ... we have the base zone index ... It must be ONE of 0, 1, 2 or 3

    int PST_hour = hour - 3 + baseZoneIndex;
    if( PST_hour < 0 ) PST_hour += 24;

    Time tmp( PST_hour, minute );

    // Now ... print out all the times ... indicating the current 'local' zone
    for( int i = sizeof myZones/sizeof myZones[0] -1; i >=0 ; --i )
    {
        tmp.Time::printTime();
        std::cout << " " << myZones[i];
        if( i == baseZoneIndex ) std::cout << " *local time* ";
        std::cout << std::endl;
        tmp.incHour();
    }
}

void Time::normalize()
{
    myAssert( (hour >= 0 && minute >= 0),
              "Error: invalid negative values ... ");
    hour += minute/60;
    minute = minute%60;
    hour = hour%24;
}

int getInt( char prompt[], int max )
{
    using std::cout;
    using std::flush;
    using std::cin;
    for( ;; ) // loop forever ... until return with a valid value ...
    {
        int tmpInt;
        cout << prompt << " : " << flush;
        cin  >> tmpInt;
        if( cin.good() && 0 <= tmpInt && tmpInt <= max )
        {
            cin.sync(); // flush cin ... and then ...
            return tmpInt; // return a valid value ...
        }
        // else ... if reach here ... have an invalid value ...
        cin.clear(); // clear error flags ... if any
        cin.sync(); // flush cin stream ...
        cout << "Valid input in range 0.." << max << " ... ";
    }
}

Or just some simpler class Time, Date ... or DateTime ... ( that shows some simple inheritance )

Code: [Select]
// this version 2010-05-17

#include <iostream>

using namespace std;

class Time //////////////////////// TIME ///////////////////////////////////////
{
public:
    Time(int hour=0, int minute=0);
    int get_hr() const;
    int get_min() const;
    void set_min(int hour);
    void set_hr(int minute);
    void print() const;
protected:
    int hr;
    int min;
};

Time::Time(int hour, int minute) : hr(hour), min(minute) {}
int Time::get_hr()const { return hr; }
int Time::get_min()const { return min; }
void Time::set_hr(int hour) { hr = hour; }
void Time::set_min(int minute) { min = minute; }
void Time::print() const
{
    if (hr < 10)  cout << "0";
    cout << hr << ":";
    if (min < 10) cout << "0";
    cout << min;
}

class Date //////////////////////// DATE ///////////////////////////////////////
{
public:
    Date(int year=2000, int month=1, int day=1);
    int get_y() const;
    int get_m() const;
    int get_d() const;
    void set_y(int year);
    void set_m(int month);
    void set_d(int day);
    void print() const;
protected:
    int y;
    int m;
    int d;
};

Date::Date(int year, int month, int day) : y(year), m(month), d(day) {}
int Date::get_y() const { return y; }
int Date::get_m() const { return m; }
int Date::get_d() const { return d; }
void Date::set_y(int year) { y = year; }
void Date::set_m(int month) { m = month; }
void Date::set_d(int day) { d = day; }
void Date::print() const
{
    if (y<9)  cout << "200";
    else if (y<99) cout << "20";
    else if (y<999) cout << "2";
    cout << y << "-";
    if (m < 10) cout << "0";
    cout << m << "-";
    if (d < 10) cout << "0";
    cout << d;
}

class DateTime : public Date, Time ////////////////// DATETIME /////////////////
{
public:
    DateTime(int year=2000, int month=1, int day=1, int hour=0, int minute=0);
    void print() const;
};

DateTime::DateTime(int year, int month, int day, int hour, int minute)
: Date(year, month, day), Time(hour, minute) {}
void DateTime::print() const
{
    Date::print();
    cout << " ";
    Time::print();
}

int main() //////////////////////////// MAIN ///////////////////////////////////

{
    Date newYearsDay2008(2008,1,1);
    cout << "New Year's Day 2008 was ";
    newYearsDay2008.print();

    // default constructor ...
    Time newYearsTime2008;
    cout << "\n\nNew Year's Time 2008 was ";
    newYearsTime2008.print();

    Date momsDay(2008,5,11);
    cout << "\n\nMother's Day 2008 was ";
    momsDay.print();
    
    DateTime newBirth(1982, 6, 11, 11, 0);
    cout << "\n\nNew Birth Event 1982 was ";
    newBirth.print();
  
    cout << "\n\nPress 'Enter' to continue ... " << flush;
    cin.get();
}
Title: Re: New thread especially for students of C and C++
Post by: David on March 25, 2009, 12:35:44 PM
Carrying on class ... now it's time to demo polymorphism and virtual class members ... (also the old C way of random file read/write of a fixed record size ... just so that you can say ... you did 'see it') ...

This project will first be presented as a driver (main) function ... Then, the xxx.h files will be listed in the same order as they are called, (and loaded into the driver file memory space - before being compiled ALL together)  ...with the driver main function file stuff ...

This is a mini data base (start) with random access binary files for an Insurance Brokerage ... that records Client (personal) info ... and Insurance (common stuff) Info ...  for all 3 (polymorph) types of insurance sold ... Home, Auto and Life.  Notice how the virtual methods/(functions) are included (higher up ... in class Insurance) so that they can be called from there, via a pointer, to point to the particular get/set info function needed for Home, Auto or Life ... Insurance

virtual void setAllInfo() =0;
   
virtual void showAllInfo() =0;

The classes Home, Auto, Life each inherit class Insurance ... and class Insurance inherits class Client ... and class Date is used to hold insurance policy start and stop dates.

There are some utility functions that I may yet incorporate as protected member functions in class Client (at the top of the hierarchy ... so that these functions can be used where needed in the other 'lower down' classes ...


Code: [Select]
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
//#include <cstring>
#include <cstdlib>

using namespace std;

const char* POLY_TYPE[] = { "HOME", "AUTO", "LIFE" };
const char* FILE_NAME[] = {"HomeData.dat", "AutoData.dat", "LifeData.dat" };
void showAllInFile( int );
int ranRead( int, int );

#include "ok_more_getPosInt_getStr.h"
#include "date.h"
#include "cli_ins_hom_aut_lif.h"


int main() //////////////////////// MAIN ///////////////////////////////////////
{
int position;
int choice;
int iType;

    // if file does not exist ... then create an empty file ...

    for(int i= 0; i<3; ++i )
    {
        ifstream fin( FILE_NAME[i], ios::in |ios::binary );
    if( !fin )
    {
            ofstream fout( FILE_NAME[i], ios::out |ios::binary );
            fout.close();
        }
        else
        {
            fin.close();
            cout << POLY_TYPE[i] << " accounts presently on file ...\n";
            showAllInFile(i);
        }
    }

    cout << ":::::TAKE-IN-INFO-LOOP:::::\n\n";
    do
    {
        iType = getPosInteger
        ( "Insurance Type (1=Home, 2=Auto, 3=Life, 4=Exit-Input-Loop): ", 0 );

        if( iType == 4) break;
        if( iType<1 || iType>3 ) continue;

        Home H; MotorVehicle M; Life L; Insurance* Ins;
        if( iType == 1 ) Ins = &H;
        else if( iType == 2 ) Ins = &M;
        else if( iType == 3 ) Ins = &L;

        int pNum =  getPosInteger( "Enter Policy Number: ", 0 );
        position = ranRead( pNum, iType );
    if( position )
        {
            cout << "The policy number already exists at position "
                 << position << endl;
            continue;
        }

        Ins->setPolicyNum( pNum );
        Ins->setPolicyType( iType );

        do{ Ins->setAllInfo(); }while( !ok() );
        do{ Ins->setAllInsInfo(); }while( !ok() );
        do{ Ins->setAllClientInfo(); }while( !ok() );

        cout << "\n\nYOUR INFORMATION WILL NOW BE SAVED ... \n";

        ofstream fout( FILE_NAME[iType-1], ios::in | ios::binary | ios::ate );
        if( iType == 1 )
            fout.write(reinterpret_cast <const char *>(&H),sizeof(H));
        else if( iType == 2 )
            fout.write(reinterpret_cast <const char *>(&M),sizeof(M));
        else if( iType == 3 )
            fout.write(reinterpret_cast <const char *>(&L),sizeof(L));
        fout.close();
        cout << "Was saved ok ... " << endl;

    }while( more() );

    cout << "\n:::::SEARCH-FOR-LOOP:::::\n\n";
    do
    {
        iType = getPosInteger
        ("Policy Number Lookup (1=Home, 2=Auto, 3=Life, 4=Exit-Look-Up-Loop): ",
          0);
        if( iType == 4 ) break;
        if( iType<1 || iType>3 ) continue;

        int tmpPolicyNum = getPosInteger( "Policy Number to view details: ", 0 );
        position = ranRead( tmpPolicyNum, iType );
        if(position) cout << " ... Found at 'position' " << position << endl;
    }while( more() );

} /////////////////////////////// END MAIN /////////////////////////////////////


void showAllInFile(int i)
{
    Insurance* Ins; Home H; MotorVehicle M; Life L;
    if( i < 0 || i > 2 ) return ;

    if( i == 0 ) Ins = &H;
    else if( i == 1 ) Ins = &M;
    else if( i == 2 ) Ins = &L;

    ifstream fin( FILE_NAME[i], ios::in |ios::binary );

    if( i == 0 )
        fin.read(reinterpret_cast <char*> (&H), sizeof(H));
    else if( i == 1 )
        fin.read(reinterpret_cast <char*> (&M), sizeof(M));
    else if( i == 2 )
        fin.read(reinterpret_cast <char*> (&L), sizeof(L));


    while( fin && !fin.eof() )
    {
        Ins->showAllClientInfo();
        Ins->showAllInsInfo();
        Ins->showAllInfo();
        cout << endl;

        if( i == 0 )
            fin.read(reinterpret_cast <char*> (&H), sizeof(H));
        else if( i == 1 )
            fin.read(reinterpret_cast <char*> (&M), sizeof(M));
        else if( i == 2 )
            fin.read(reinterpret_cast <char*> (&L), sizeof(L));
    }
    fin.close();
    cout << endl;
}

// returns position 1...n   OR   0 if not found
int ranRead( int PolicyNum, int iType )
{
    int position=0;

    ifstream fin( FILE_NAME[iType-1], ios::in |ios::binary );
    if( !fin )
    {
        cerr << "File " << FILE_NAME[iType-1]
             << " Not Found ... Press 'Enter' to continue ... " << flush;
        cin.sync();
        cin.get();
        exit(1);
    }

    bool found = false;
    Insurance* Ins; Home H; MotorVehicle M; Life L;
    if( iType == 1 )
    {
        Ins = &H;
        fin.read(reinterpret_cast <char*> (&H), sizeof(H));
        while( fin && !fin.eof() )
        {
            if(Ins->getPolicyNum() == PolicyNum)
            {
                position = fin.tellg()/sizeof(H);
                found = true;
                break;
            }
            fin.read(reinterpret_cast <char*> (&H), sizeof(H));
        }
    }
    else if( iType == 2 )
    {
        Ins = &M;
        fin.read(reinterpret_cast <char*> (&M), sizeof(M));
        while( fin && !fin.eof() )
        {
            if(Ins->getPolicyNum() == PolicyNum)
            {
                position = fin.tellg()/sizeof(M);
                found = true;
                break;
            }
            fin.read(reinterpret_cast <char*> (&M), sizeof(M));
        }
    }
    else if( iType == 3 )
    {
        Ins = &L;
        fin.read(reinterpret_cast <char*> (&L), sizeof(L));
        while( fin && !fin.eof() )
        {
            if(Ins->getPolicyNum() == PolicyNum)
            {
                position = fin.tellg()/sizeof(L);
                found = true;
                break;
            }
            fin.read(reinterpret_cast <char*> (&L), sizeof(L));
        }
    }

    if( !found )
    {
        cout << "\nRecord Not Found!!\n";
    }
    else
    {
        Ins->showAllClientInfo();
        Ins->showAllInsInfo();
        Ins->showAllInfo();
        cout << endl;
    }

    return position;
}
Title: Re: New thread especially for students of C and C++
Post by: David on March 25, 2009, 12:43:14 PM
Now ... the xxx.h files ... in the order that they are loaded into the driver main function (as per above program) ...

Code: [Select]
// ok_more_getPosInt_getStr.h


bool more() // defaults to 'yes'
{
    std::cout << "\nMore ... (y/n) ? " << std::flush;
    int reply = std::cin.get();
    std::cin.sync();
    return( !(reply=='n' || reply=='N') );
}

bool ok() // defaults to 'no'
{
    std::cout << "\nOk ... (y/n) ? " << std::flush;
    int reply = std::cin.get();
    std::cin.sync();
    return( reply=='y' || reply=='Y' );
}

int getPosInteger( char* prompt, int min ) // returns a 'valid' integer greater than 'min'
{
    int tmp;
    for( ;; ) // loop forever ... until return a valid value ...
    {
        std::cout << prompt << std::flush;
        tmp = -1;
        std::cin >> tmp;
        if( !std::cin.good() || tmp <= min )
        {
            std::cin.clear(); // clear arror flag (if any) ...
            std::cin.sync(); // flush cin stream ...
            std::cout << "\nError! Enter integers greater than " << min
                      << " only: " << std::endl;
            continue; // continue right now from the top of the forever loop ...
        }

        // else if reach here ...
        std::cin.sync();
        return tmp; // return a valid value ...
    }
}

void getStr( char buf[], int max )
{
    for( ;; )
    {
        std::string tmp;
        getline( std::cin, tmp );
        if( tmp.length() < max ) // copy into c[]
        {
int i;
            for(i=0; i<(int)tmp.length(); ++i) buf[i] = tmp[i];
buf[i] = 0; // null terminate
            return;
        }

        //else ... if reach here ... the string input was too loog ... so
        std::cout << std::setw(max-1) << std::setfill('x') << 'x'
                  << std::setfill(' ')
                  << " Max " << max-1 << " characters ... try again:"
                  << std::endl;
    }
}



Code: [Select]
// "date.h"

class Date
{
private:
int day;
int month;
int year;
public:
Date(); //set all data to zero
Date (int d, int m, int y);
Date (Date&); //copy constructor -  copies a date
~Date(); // destructor

//accessors
int getDay();
int getMonth();
int getYear();

//mutators
void setDay(int d);
void setMonth(int m);
void setYear(int y);

void showDate();
};


Date:: Date() //default constructor
{
day = 0;
month = 0;
year = 0;
}

Date:: Date(int d, int m, int y) //primary constructor
{
day = d;
month = m;
year = y;
}

Date:: Date(Date& d) //copy construtor
{
day = d.day;
month = d.month;
year = d.year;
}

Date:: ~Date() // destructor
{
    //cout<< "Date Object Destroyed\n";
}

int Date:: getDay()
{
return day;
}
int Date:: getMonth()
{
return month;
}
int Date:: getYear()
{
return year;
}

void Date:: setDay(int d)
{
day = d;
}
void Date:: setMonth(int m)
{
month = m;
}
void Date:: setYear(int y)
{
year = y;
}

void Date:: showDate()
{
cout << setfill('0')
         << setw(2)<< day << "/"
         << setw(2) << month << "/"
         << setw(2) << year << setfill(' ');
}


Code: [Select]
// "cli_ins_hom_aut_lif.h"

class Client ///////////////////////////// Client //////////////////////////////
{
protected:
    char FirstName[25];
    char LastName[25];
    char Address1[32];
    char Address2[32];
    char Gender[7];
    int Age;

public:
    Client();
    ~Client();

    //Accessors
    char* getFirstName();
    char* getLastName();
    char* getAddress1();
    char* getAddress2();
    char* getGender();
    int getAge();

    void setAllClientInfo();
    void showAllClientInfo();
};

Client::Client()
{
    FirstName[0]=0;
    LastName[0]=0;
    Address1[0]=0;
    Address2[0]=0;
    Gender[0]=0;
    Age = 0;
}
Client::~Client() { /* cout<<"Client Object Destroyed!\n"; */ }

char* Client::getFirstName() { return FirstName; }
char* Client::getLastName() { return LastName; }
char* Client::getAddress1() { return Address1; }
char* Client::getAddress2() { return Address2; }
char* Client::getGender() { return Gender; }
int Client::getAge() { return Age; }

void Client::setAllClientInfo()
{
    cout << "\n:::::CLIENT INFORMATION:::::\n\n";

    cout << "Please Enter Your First Name: ";
    getStr( FirstName, sizeof(FirstName) );
    cout << "Please Enter Your Last Name: ";
    getStr( LastName, sizeof(LastName) );

    Age = getPosInteger( "Please Enter Age: ", 0 );

    cout << "Please Enter Gender: ";
    getStr( Gender, sizeof(Gender) );
    cout << "Please Enter First Line Of Address: ";
    getStr( Address1, sizeof(Address1) );
    cout << "Please Enter Second Line Of Address: ";
    getStr( Address2, sizeof(Address2) );
}
void Client::showAllClientInfo()
{
    cout <<"\n" << LastName << ", " << FirstName << ", "
         << Gender << ", " << Age << ", "<<Address1 << ", " <<Address2;

}////////////////////////////////////// END CLIENT /////////////////////////////

class Insurance: public Client ///////// INSURANCE /////////////////////////////
{
protected:
    int PolicyNum;
    int PolicyType; // 1 = Home; 2 = Auto; 3 = Life
    Date EffectiveDate;
    Date ExpiryDate;
    int Premium;

public:
    Insurance();
    ~Insurance();

    int getPolicyNum();
    int getPolicyType();
    int getPremium();

    void setEffectiveDate(Date&);
    void setExpiryDate(Date&);
    void setPolicyType(int);
    void setPolicyNum(int);

    virtual void setAllInfo() =0;
    void setAllInsInfo();
    void showAllInsInfo();
    virtual void showAllInfo() =0;
};

Insurance::Insurance() : EffectiveDate(0,0,0), ExpiryDate(0,0,0)
{ PolicyNum=0; PolicyType=0; Premium=0; }
Insurance::~Insurance()
{ /* cout<<"Insurance Object Destroyed!\n"; */ }

int Insurance::getPolicyNum() { return PolicyNum; }
int Insurance::getPolicyType() { return PolicyType; }
int Insurance::getPremium() { return Premium; }

void Insurance::setEffectiveDate(Date& Edate)
{
    EffectiveDate.setDay(Edate.getDay());
    EffectiveDate.setMonth(Edate.getMonth());
    EffectiveDate.setYear(Edate.getYear());
}
void Insurance::setExpiryDate(Date& Exdate)
{
    ExpiryDate.setDay(Exdate.getDay());
    ExpiryDate.setMonth(Exdate.getMonth());
    ExpiryDate.setYear(Exdate.getYear());
}

void Insurance::setPolicyType( int tNum ) { PolicyType = tNum; }
void Insurance::setPolicyNum( int pNum) { PolicyNum = pNum; }

void Insurance::setAllInsInfo()
{
    int y, m, d;
    for( ;; )
    {
        cout << "\n:::::INSURANCE INFORMATION:::::\n\n";
        cout << "For Policy#: " << PolicyNum << endl;

        cout << "Enter date yy/mm/dd for policy to become effective: ";
        y=m=d=0;
        char c;
        cin >>y>>c>>m>>c>>d;
        if( !cin.good() )
        {
            cin.clear();
            cin.sync();
            cout << "Entry error ...\n" << flush;
            continue;
        }
       
        Date efd( d, m, y );
        setEffectiveDate( efd );

        cout << "Enter date yy/mm/dd for policy to expire: ";
        y=m=d=0;
        cin >>y>>c>>m>>c>>d;
        if( !cin.good() )
        {
            cin.clear();
            cin.sync();
            cout << "Entry error ...\n" << flush;
            continue;
        }
       
        break;
    }
   
    Date exd( d, m, y );
    setExpiryDate( exd );

    Premium = getPosInteger
    ( "Insurance Annual Premium (rounded to nearest dollar) $", 0 );

}

void Insurance::showAllInsInfo()
{
    // char* POLY_TYPE[] = { "Home", "Auto", "Life" }; // now a global const
    cout << "\nPolicy Number    : " << PolicyNum;
    // 1 = Home; 2 = Auto; 3 = Life
    cout << "\nPolicy Type      : " << POLY_TYPE[PolicyType-1];
    cout << "\nEffective Date   : ";
    EffectiveDate.showDate();
    cout << "\nExpiry Date      : ";
    ExpiryDate.showDate();
    cout << "\nPremium          : " << Premium;

} ///////////////////////////// END INSURANCE //////////////////////////////////

class Home : public Insurance //////////////////// HOME ////////////////////////
{
private:
    int HomeValue;
    char RoofType[15];
    char Question1[4];//is a bugler alarm installed
    char Question2[4];//is a fire alarm installed

public:
    Home()
    {
        HomeValue = 0;
        RoofType[0]=0;
        Question1[0]=0;
        Question2[0]=0;
    }
    ~Home() { /* cout<<"Home Object Destroyed!\n"; */ }

    //Accessors
    int getHomeValue() { return HomeValue; }
    char* getRoofType() { return RoofType; }
    char* getQuestion1() { return Question1; }
    char* getQuestion2() { return Question2;}

    void setAllInfo()
    {
        cout << "\n:::::HOME INSURANCE INFORMATION:::::\n\n";

        HomeValue = getPosInteger( "Please Enter Home Value: $", 0 );

        cout << "Enter Roof Type: ";
        getStr( RoofType, sizeof(RoofType) );
        cout << "Is a Burgler Alarm Installed? ";
        getStr( Question1, sizeof(Question1) );
        cout << "Is a Fire Alarm Installed? ";
        getStr( Question2, sizeof(Question2) );
    }

    void showAllInfo()
    {
        cout << "\nHome Value       : $" << HomeValue;
        cout << "\nRoof Type        : " << RoofType;
        cout << "\nBurgler Alarm Installed: " << Question1;
        cout << ", Fire Alarm Installed: " << Question2;
    }
}; /////////////////////////////// END HOME ////////////////////////////////////


class MotorVehicle : public Insurance ///////////////// AUTO ///////////////////
{
private:
    char Model[20];
    char Make[15];
    int Year;
    char LicenseNum[10];
    char ChassisNum[10];
    int EstimatedValue;
    char FuelType[9];
    char BodyType[9];
    char CCRating[4];
    int SeatCapacity;
    char Question1[4];//was ... involved in an accident during the last year
    char Question2[4];//does ... have a burgler alarm installed

public:
    MotorVehicle()
    {
        Model[0]=0;
        Make[0]=0;
        Year = 0;
        LicenseNum[0]=0;
        ChassisNum[0]=0;
        EstimatedValue = 0;
        FuelType[0]=0;
        BodyType[0]=0;
        CCRating[0]=0;
        SeatCapacity = 0;
        Question1[0]=0;
        Question2[0]=0;
    }

    ~MotorVehicle() { /* cout<<"Vehicle Insurance Destroyed!"; */ }

    void setAllInfo()
    {
        cout << ":::::MOTOR VEHICLE INSURANCE INFORMATION:::::\n\n";

        cout << "Please Enter Make of Vehicle: ";
        getStr( Make, sizeof(Make) );
        cout << "Please Enter Model of Vehicle: ";
        getStr( Model, sizeof(Model) );

        Year = getPosInteger( "Please Enter Vehicle Year: ", 0 );

        cout << "Enter License#: ";
        getStr( LicenseNum, sizeof(LicenseNum) );
        cout << "Enter Chassis#: ";
        getStr( ChassisNum, sizeof(ChassisNum) );

        EstimatedValue = getPosInteger
        ( "Please Enter A Estimated Value Of The Vehicle: $", 0 );

        cout << "Enter Fuel Type: ";
        getStr( FuelType, sizeof(FuelType) );
        cout << "Enter Body Type: ";
        getStr( BodyType, sizeof(BodyType) );

        SeatCapacity = getPosInteger( "Enter Seat Capacity: ", 0 );

        cout << "Enter CC Ratings: ";
        getStr( CCRating, sizeof(CCRating) );
        cout << "Was The Vehicle Involved In An Accident During The Last Year? ";
        getStr( Question1, sizeof(Question1) );
        cout << "Does The Vehicle Have A Burgler Alarm Installed? ";
        getStr( Question2, sizeof(Question2) );
    }

    void showAllInfo()
    {
        cout << "\nMake of Vehicle  : " << Make;
        cout << "\nModel of Vehicle : " << Model;
        cout << "\nVehicle Year     : " << Year;
        cout << "\nEnter License#   : " << LicenseNum;
        cout << "\nEnter Chassis#   : " << ChassisNum;
        cout << "\nEstimated Value  :$" << EstimatedValue;
        cout << "\nFuel Type        : " << FuelType;
        cout << "\nBody Type        : " << BodyType;
        cout << "\nSeat Capacity    : " << SeatCapacity;
        cout << "\nCC Ratings       : " << CCRating;
        cout << "\nAccident in Year : " << Question1;
        cout << "\nAlarm Installed  : " << Question2;
    }
}; /////////////////////////////////////// END AUTO ////////////////////////////

class Life :public Insurance ////////////// LIFE ///////////////////////////////
{
private:
    char Occupation[20];
    char Question1[4];//Is The Insured Person A Smoker?
    char Question2[4];//Is accidential Death And Disenberment Coverage Required?
    int EstimatedValue;

public:
    Life()
    {
        Occupation[0]=0;
        Question1[0]=0;
        Question2[0]=0;
        EstimatedValue = 0;
    }

    ~Life() { /* cout<<"\nLife Insurance Destroyed!\n"; */ }

    void setAllInfo()
    {
        cout << ":::::LIFE INSURANCE INFORMATION:::::\n\n";

        cout << "Occupation? ";
        getStr( Occupation, sizeof(Occupation) );
        cout << "Is The Insured Person A Smoker? ";
        getStr( Question1,sizeof(Question1) );
        cout << "Is Accidential Death And Disenberment Coverage Required? ";
        getStr( Question2, sizeof(Question2) );

        EstimatedValue = getPosInteger( "Estimated Coverage Required? ", 0 );
    }

    void showAllInfo()
    {
        cout << "\nOccupation       : " << Occupation;
        cout << "\nSmoker           : " << Question1;
        cout << "\nADAD Coverage    : " << Question2;
        cout << "\nLife Coverage    : " << EstimatedValue;
    }
};
Title: Re: New thread especially for students of C and C++
Post by: David on March 27, 2009, 05:49:13 PM
Taking another little break in pace ... C this fun stuff for C beginners ... (  some common beginning problems ... eh :)  ) ... but NO longer a problem ?


How do you ask for more in C  ... in a loop

C this ...

Code: [Select]
/* validating numeric entry in C and use of 'do( ... )while( more() );' loop */

#include <stdio.h>
#include <ctype.h>

int more() /* defaults to yes ... unless 'n' or 'N' are entered */
{
    int reply;
    printf("\nMore ... (y/n) ? ");
    reply = toupper( getchar() );
    if( reply = '\n' )
        while( getchar() != '\n' ) ; /* flush stdin ...*/
    return reply != 'N';
}

int main() /* testing more() ...*/
{
    int i, numGood;
    do
    {
        printf("Enter a positive integer : ");
        numGood = scanf("%d", &i );
        while( getchar() != '\n' ) ; /* flush stdin ... */
        if( numGood != 1 || i < 0 )
        {
            if( numGood != 1) puts("\nEntry error! INTEGERS only please ...");
            if( i < 0 ) puts("\nEntry error! POSITIVE integers only please ...");
            continue;
        }

        printf("Yeah ... you can read and type.  You entered %d\n", i);
       
    }while( more() );
   
    return 0;
}


This demonstrates using more() in a real student problem ...

When given a 4 digit integer such as 5432, I want to parse each individual number out of the whole number and add them together. For example: 5432 becomes 5 + 4 + 3 + 2 = 14

Oops ... this is coded in C++ ... but, by now, you may be able to easily convert the cout's to C stdio io's.  Enjoy ... the fun with recursion ...

Code: [Select]
// some fun with recursion ...
// finding the sum of digits in a number
// 5432 becomes 5 + 4 + 3 + 2 = 14


#include <iostream>
using namespace std;

void showSumAndDigits( int num, int& sum )
{
    if(num == 0)
    {
        cout << sum << " = ";
        return;
    }
   
    int nextDigit = num%10;
    sum += nextDigit;
    showSumAndDigits( num/10, sum );
    if( num/10 )
        cout << " + ";
    cout << nextDigit;
}

void showDigitsFindSum( int num, int& sum )
{
    if(num == 0) return;

    int nextDigit = num%10;
    sum += nextDigit;
    showDigitsFindSum( num/10, sum );
    if( num/10 )
        cout << " + ";
    cout << nextDigit;
}


void showNumDigitsSum( int num )
{
    int sum = 0;
    cout << "Recursive sum of digits in " << num << " becomes ";
    showDigitsFindSum( num, sum );
    cout << " = " << sum;
}



int main()
{
    cout << "Enter a number to see the digits added : " << flush;
    int num, sum = 0;
    cin >> num;
    int numCopy = num;
    while(num)
    {
        int nextDigit = num%10;
        sum += nextDigit;
        cout << "Next digit: " << nextDigit << " Next sum: " << sum << endl;
        num /= 10;
    }
   
    cout << "\n\nRecursive sum of digits in " << numCopy << " becomes ";
    sum = 0;
    showSumAndDigits( numCopy, sum );
   
    cout << "\n\nshowNumDigitsSum( " << numCopy << " ) ... \n";
    showNumDigitsSum(numCopy);
   
    cout << "\n\nPress 'Enter' to exit ... " << flush;
    cin.sync();
    cin.get();
}


An other common student question ...

I have been asked to change the code so that a program asks repeatedly for a new number of Fibonacci numbers until the user decides to quit. The user's decision to continue or not is taken within a simple user-defined function. Design this 'more' function to take no parameters, but to return a boolean value ... (but in C we use type int and return 0=false or 1 or some non zero value for true). The function should return 'true' when the user types 'Y' or 'y', false when the user enters 'N' or 'n' ... (but default to 'yes/true' ... that is ... if any key, including just the 'Enter'Return key is pressed ... excepting 'n' or 'N' ... the function will return 'true' / 'yes'.  But ... to get 'false' / 'no' ... one MUST actually enter 'n' or N').

Here is a start ... (that also demo's a C way to validate numeric input) ...

Code: [Select]
/* validating numeric entry in C and use of 'do( ... )while( more() );' loop */

#include <stdio.h>
#include <ctype.h>

int more() /* defaults to yes ... unless 'n' or 'N' are entered */
{
    int c, reply;
    printf("\nMore ... (y/n) ? ");
    reply = c = toupper( getchar() );
    while( c != '\n' ) c = getchar(); /* flush stdin ...*/
    return reply != 'N';
}

int main() /* testing more() ...*/
{
    int i, numGood;
    do
    {
        printf("Enter a positive integer : ");
        numGood = scanf("%d", &i );
        while( getchar() != '\n' ) ; /* flush stdin ... */
        if( numGood != 1 || i < 0 )
        {
            if( numGood != 1) puts("\nEntry error! INTEGERS only please ...");
            if( i < 0 ) puts("\nEntry error! POSITIVE integers only please ...");
            continue;
        }

        printf("Yeah ... you can read and type.  You entered %d\n", i);

    }while( more() );
   
    return 0;
}


The Fibonacci Number series ... may also be found by recursive function calls ... ( but the most frequent, real use of recursion, that I know, is for Quick Sort ... which we may get to ... yet :) )

Code: [Select]
#include <stdio.h>

#define START_VAL 10

#define info "The following will find the Fibonacci series ...\n\n" \
             "Notice that it will take progressively longer to\n" \
             "find the next term, if you ask for much more than about\n" \
             "40 or so terms ...depending on the speed of your CPU.\n\n" \
             "Also ... there is an upper limit (46) to the number of terms\n" \
             "that may be accurately found, depending on the number of bits\n" \
             "in an 'int' used by your Compiler, (if 32 bits, as often, in 2008).\n\n"
/*
    Fibonacci series:  with recursive calls
    F(1) = 1
    F(2) = 1
    F(3) = F(2) + F(1) = 1 + 1 = 2
    F(4) = F(3) + F(2) = 2 + 1 = 3

    by D.W.Zavitz
    http://developers-heaven.net/forum/index.p...opic,127.0.html
    http://developers-heaven.net/forum/index.p...opic,106.0.html
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

int fib( int n )
{
    /*
        To handle the special cases: fib(2)=1; fib(1)=1;
        THIS SPECIAL definition for fib(2) and fib(1) ...
        is properly HANDLED by the next statement:
    */
    if (n <= 2 ) return 1;

    /*
        The following will push onto the 'stack memory'
        ALL (function) pairs like this:
        <fib(n-1), fib(n-2)>, .... <fib(3),fib(2)>,  <fib(2),fib(1)>
        BUT WE then have put on the stack
        THAT fib(2) is 1 and fib(1) is 1 ....
        So ... (popping the stack on return ...)
        'pop' (i.e. TAKE) from the TOP of stack ...
        starting with fib(1) = 1 and fib(2) =1

        WE NOW KNOW how to RETURN all these values:

        fib(1)= 1; // special well defined case
        fib(2)= 1; // special well defined case
        fib(3) = fib(2)+fib(1) = 2; // recursively defined cases begin ...
        fib(4) = fib(3)+fib(2) = 3;
        fib(5) = fib(4)+fib(3) = 5;
        fib(6) = fib(5)+fib(4) = 8;
        .
        .
        .
        fib(n) = fib(n-1) + fib(n-2)
    */

    else return fib(n-1) + fib(n-2);
}

int more() /* defaults to yes ... unless 'n' or 'N' are entered */
{
    int c, reply;
    printf("\nMore ... (y/n) ? ");
    reply = c = toupper( getchar() );
    while( c != '\n' ) c = getchar(); /* flush stdin ...*/
    return reply != 'N';
}


int main()
{
    puts( info );
    int i, j, numGood;
    printf("Fibronacci series for the first %d numbers.\n", START_VAL);
    for( i=1; i<=START_VAL; ++i ) printf("%d ", fib(i));
    puts("\n");
    do{
        j = -1;
        printf("Your number please : ");
        numGood = scanf("%d", &j);
        while( getchar() != '\n' ) ; /* flush stdin ... */
        if( numGood ==1 && j > 0 )
        {
            if( j > 40 ) { puts("You've got to be kidding ... eh?"); continue; }
            for( i=1; i<=j; ++i ) printf("%d ", fib(i));
        }
        else
        {
            puts("\nValid input is 'only integers greater than 0' ...");
        }
       
    }while( more () );

    return 0;
}


Future ... (Quick Sort vs other sorts ?) ... but now, an analysis of 3 versions of Quick Sort ...

First version ...

Code: [Select]
/*
    Analysis of quick sort of an array of a few 'random' int's ...
    (using dynamic memory to hold the arrays) ...
*/

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

#define MAX 14 /* change here ... to whatever number you desire ... */

/* forward declarations ... */

void qSort(int a[], int left, int right);
int partition(int a[], int left, int right);
void swap(int* a, int* b);

void pause(char* message);


/*******************************************************************************
    begin main ...
*******************************************************************************/
int main()
{
    int i, tmp;
    int* ary = (int*) malloc( sizeof(int) * MAX );
    srand( time(NULL) );
    for(i = 0; i<MAX; ++i) ary[i] = rand()%MAX + 1;
    for(i = 0; i<MAX; ++i) printf("%d ", ary[i]);
    printf("Unsorted ... ");
    pause("continue");
   
    qSort(ary, 0, MAX-1);
    for(i = 0; i<MAX; ++i) printf("%d ", ary[i]);
    printf("Quick sorted ... ");   
    pause("continue");
   
    puts("\nMostly sorted ...");
    /* swap first and last ... */
    tmp = ary[0];
    ary[0] = ary[MAX-1];
    ary[MAX-1] = tmp;
   
    for(i = 0; i<MAX; ++i) printf("%d ", ary[i]);
    printf("One-un-sorted ... ");
    pause("continue");
   
    qSort(ary, 0, MAX-1);
    for(i = 0; i<MAX; ++i) printf("%d ", ary[i]);
    printf("Quick sorted ... ");   
    pause("EXIT");   
   
    free(ary);
    return 0;
}
/*******************************************************************************
    ... end main
*******************************************************************************/


void qSort(int a[], int left, int right)
{
    if(left >= right) return;
    int pivot = partition(a, left, right);
    qSort(a, left, pivot-1);
    qSort(a, pivot+1, right);
}

int partition(int a[], int left, int right)
{
    static int count =0;
int first = left, pivot = right--;
int i, tmp;
while(left <= right)
{
while(a[left] < a[pivot]) left++;
while( right >= first && a[right] >= a[pivot]) right--;
if(left < right)
{
/* swap( &a[left], &a[right] ); */
/* swap done 'inline' here ... */
tmp = a[left];
a[left] = a[right];
a[right] = tmp;

left++;

for(i = 0; i<MAX; ++i) printf("%d ", a[i]);
printf("... %2d left=%2d pivot=%2d right=%2d\n",++count,left,pivot,right);
}
}

/* when we reach here ... left > right ... */

/* swap done 'inline' here ... */
if(left != pivot) /* swap( &a[left], &a[pivot] ); */
{
tmp = a[left];
a[left] = a[pivot];
a[pivot] = tmp;

        for(i = 0; i<MAX; ++i) printf("%d ", a[i]);
printf("*** %2d left=%2d pivot=%2d right=%2d\n",++count,left,pivot,right); 
    }
   
return left;
}

void swap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void pause(char* message)
{
    printf("Press Enter to %s ...", message);
    while( getchar() !='\n' ) ; /* flush stdin ... */
}


Three methods of qsort compared ...

Code: [Select]
/*
    Analysis of quick sort of an array of a few 'random' int's ...
    (using dynamic memory to hold the arrays) ...
*/

/* this version 2010-05-17 */

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

#define debug 0    /* if you set these to 1 ... */
#define display 0  /* make sure you reduce MAX to about 14 or so ...*/

#define MAX 100000 /* change here ... to whatever number you desire ... */

/* forward declarations ... */

int count; /* global variable */

/* compare method 2 ...*/
void qSort2(int a[], int left, int right);
/* with method 1 ...*/
void qSort(int a[], int left, int right);
int partition(int a[], int left, int right);
void swap(int* a, int* b);

void pause( char* message );
void show( int a[] );

/* for method 3 ... stdlib.h qsort( ... ) ... */
int compare( const void* x, const void* y )
{
    return( *(int*)x - *(int*)y );
}

/*******************************************************************************
    begin main ...
*******************************************************************************/
int main()
{
    int* ary = (int*) malloc( sizeof(int)*MAX );
    int* ary2 = (int*) malloc( sizeof(int)*MAX );
    if( ary == NULL || ary2 == NULL )
    {
        fputs( "Error: malloc failed ... Press 'Enter' to EXIT ... ", stderr );
        getchar();
        return 1;
    }
    else
    {
        time_t t1, t2;
        int i, tmp, bot, top;
        srand( time(NULL) );
        for(i = 0; i<MAX; ++i) ary[i] = rand()%MAX + 1;
        /* get a copy of array 'ary' into array 'ary2' ...*/
        for(i = 0; i<MAX; ++i) ary2[i] = ary[i];

        /* show unsorted array ... */
        printf("Unsorted:\n");
        show( ary );
        //pause("continue");

        count=bot=0;
        top=MAX-1;
        t1 = clock();
        qSort(ary, bot, top);
        t2 = clock();

        /* show after qsort ... */
        printf("Quick sorted:\n");
        show( ary );
        printf( "time for sort was %ld\n", t2-t1 );
        //pause("continue");


        /* swap first and last ... */
        tmp = ary[0];
        ary[0] = ary[MAX-1];
        ary[MAX-1] = tmp;

        printf("Two-un-sorted:\n");
        show( ary );
        //pause("continue");

        count=bot=0, top=MAX-1;
        t1 = clock();
        qSort(ary, bot, top);
        t2 = clock();

        printf("Quick sorted:\n");
        show( ary );
        printf( "time for sort was %ld\n", t2-t1 );
        //pause("continue to 2");



        for(i = 0; i<MAX; ++i) ary[i] = ary2[i];
        puts("\n\nversion 2 ...");

        printf("\nUnsorted:\n");
        show( ary );
        //pause("continue");

        count=bot=0, top=MAX-1;
        t1 = clock();
        qSort2(ary, bot, top);
        t2 = clock();

        printf("Quick sorted:\n");
        show( ary );
        printf( "time for sort was %ld\n", t2-t1 );
        //pause("continue");


        /* swap first and last ... */
        tmp = ary[0];
        ary[0] = ary[MAX-1];
        ary[MAX-1] = tmp;

        printf("Two-un-sorted:\n");
        show( ary );
        //pause("continue");

        count=bot=0, top=MAX-1;
        t1 = clock();
        qSort2(ary, bot, top);
        t2 = clock();

        printf("Quick sorted:\n");
        show( ary );
        printf( "time for sort was %ld\n", t2-t1 );



        for(i = 0; i<MAX; ++i) ary[i] = ary2[i];
        puts("\n\nversion 3 ...");

        printf("\nUnsorted:\n");
        show( ary );
        //pause("continue");

        count=bot=0, top=MAX-1;
        t1 = clock();
        qsort(ary, MAX, sizeof(int), compare);
        t2 = clock();

        printf("Quick sorted:\n");
        show( ary );
        printf( "time for sort was %ld\n", t2-t1 );
        //pause("continue");


        /* swap first and last ... */
        tmp = ary[0];
        ary[0] = ary[MAX-1];
        ary[MAX-1] = tmp;

        printf("Two-un-sorted:\n");
        show( ary );
        //pause("continue");

        count=bot=0, top=MAX-1;
        t1 = clock();
        qsort(ary, MAX, sizeof(int), compare);
        t2 = clock();

        printf("Quick sorted:\n");
        show( ary );
        printf( "time for sort was %ld\n", t2-t1 );

        pause("EXIT");

        free(ary2);
        free(ary);
        return 0;
    }
}
/*******************************************************************************
    end main
*******************************************************************************/


void qSort2(int a[], int left, int right)
{
    int lft=left, rht=right;
    int tmp, comp = a[(lft+rht)/2];
    do
    {
        while(a[lft] < comp) ++lft;
        while(a[rht] > comp) --rht;
        if(lft <= rht) // swap
        {
#if debug
            int i;
            for(i = 0; i<MAX; ++i) printf("%d ", a[i]);
printf("... %2d lft=%2d comp=%2d rht=%2d\n",++count,lft,comp,rht);
#endif
            tmp = a[lft];
            a[lft] = a[rht];
            a[rht] = tmp;
            ++lft; --rht;
        }
    }while(!(lft>rht));
    if(left < rht) qSort2( a, left, rht);
    if(lft < right) qSort2( a, lft, right);
}

void qSort(int a[], int left, int right)
{
    if(left >= right) return;
    int pivot = partition(a, left, right);
    qSort(a, left, pivot-1);
    qSort(a, pivot+1, right);
}

int partition(int a[], int left, int right)
{
    int tmp;
    int first = left, pivot = right--;
while(left <= right)
{
while(a[left] < a[pivot]) left++;
while( right >= first && a[right] >= a[pivot]) right--;
if(left < right)
{
/* swap( &a[left], &a[right] ); */
tmp = a[left];
a[left] = a[right];
a[right] = tmp;

left++;
#if debug
            int i;
for(i = 0; i<MAX; ++i) printf("%d ", a[i]);
printf("... %2d left=%2d pivot=%2d right=%2d\n",
                   ++count,left,pivot,right);
#endif
}
}

/* when we reach here ... left > right ... */

if(left != pivot) //swap( &a[left], &a[pivot] );
{
tmp = a[left];
a[left] = a[pivot];
a[pivot] = tmp;
#if debug
        int i;
        for(i = 0; i<MAX; ++i) printf("%d ", a[i]);
printf("*** %2d left=%2d pivot=%2d right=%2d\n",
               ++count,left,pivot,right);
#endif
    }

return left;
}

void swap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void pause( char* message )
{
    printf("Press Enter to %s ...", message);
    while( getchar() !='\n' ) ; /* flush stdin ... */
}

void show( int a[] )
{
#if display
    int i;
    for(i = 0; i<MAX; ++i) printf("%d ", a[i]);
    putchar( '\n' );
#endif
}


Here is a demo of qsort (of arrays) in C ... of int's, C string's, struct's ...
(and also demo's sorts that ignore case - click on following link to jump there now)

http://developers-heaven.net/forum/index.php/topic,106.msg277.html#msg277 (http://developers-heaven.net/forum/index.php/topic,106.msg277.html#msg277)
Title: Re: New thread especially for students of C and C++
Post by: David on March 27, 2009, 08:45:03 PM
Finally ... C a linked list ... (with an insertion sort method that adds new list elements by inserting them in ABC...abd... sorted order.

Also ... uses typedef char* CString ....  A CString holds a whole line of input ... and using a 'list' of lines ... you can have as many lines as you may wish ... as much as available memory permits ...  in your 'insertion-sorted list'.  This *IS* an often seen student problem ... but no more a problem ... eh :)

Code: [Select]
/* ListOfStr.c */ /* this version 2010-09-07 */

/*
    latest rev. maintained at:
    http://developers-heaven.net/forum/index.php/topic,106.msg257.html#msg257
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* re. strlen, strcpy, strcmp ... */

/* make this big enough so ALL C strings DO have the 'fgets' '\n' char at end */
#define BUFFER_SIZE 1024

typedef char* CString;
typedef struct ListOfStr
{
    CString str;                /* 'str' holds the address of a new C string ... */
    struct  ListOfStr* next;    /* points to the next C string ... */
}ListOfStr;

typedef ListOfStr* pListOfStr;  /* to hold the address of a C string */


/* prototypes ... */

/* INSERTS C string 'data' into 'list' in 'ABC...abc...' order */
pListOfStr insert_list( char data[], pListOfStr list );

/* erase/free up all memory in list */
pListOfStr erase_list( pListOfStr list );

/* show all the (insert_list-sorted) 'text lines' in the 'list' */
void print_list( pListOfStr p );

/* utility function to return a copy ... in NEWly allocated memory */
CString newCopy( char str[] );

void myAssert( int condition, char text[] )
{
    if( !condition )
    {
        fprintf( stderr, "%s.  Press 'Enter' to exit ... ", text );
        getchar();
        exit(1);
    }
}



int main() /* * * * * * * * * * * * * * MAIN BEGINS * * * * * * * * * * * * * */
{
    /* in the following ... list is declared as a pointer to ListOfStr ... */
    pListOfStr list = NULL; /* 'list' ... holds the address of the list's HEAD */

    char buffer[BUFFER_SIZE];
    char prompt[] = "Enter line of text to insert_list into list (or Ctrl Z to end) : ";

    while( printf( prompt ) && fgets(buffer, BUFFER_SIZE, stdin) != NULL  )
        list = insert_list( buffer, list );

    print_list( list );

    list = erase_list( list ); /* ... and re-sets list to NULL ... */

    printf( "\nPress 'Enter' ... to continue ... " );
    getchar();
    return 0;

}/* * * * * * * * * * * * * * * * * * MAIN ENDS * * * * * * * * * * * * * * * */



/* pass in the address of the 'head' ... and the line to insert_list ...*/
pListOfStr insert_list( char data[], pListOfStr list )
{
    pListOfStr p, q;

    /* create a new node */
    p = (pListOfStr) malloc( sizeof(ListOfStr) );
    myAssert( (p != NULL), "Error: malloc failed to allocate memory (1)" );

    /* save address of new memory (copy of data) into the new node ... */
    p->str = newCopy( data );

    /* first, we handle the case where 'data' should be the first element */
    if( list == NULL || strcmp(data, list->str) < 0 )
    {
        /* yes ... this IS the first element */
        /* so set to be the first element */
        p->next = list;
        return p;
    }

    /* if program reaches here, search the linked list for the right location */
    q = list;
    while( q->next != NULL && strcmp(q->next->str, data) <= 0 )
        q = q->next;

    p->next = q->next;
    q->next = p;
    return list;
}

pListOfStr erase_list( pListOfStr list )
{
    pListOfStr p;
    while( list != NULL )
    {
        p = list->next;
        free( list->str );
        free( list );
        list = p;
    }
    return list; /* i.e. return NULL; */
}

/*
    Note: all these lines (strings) have a '\n' char at the end ... except
    maybe, the last line ... (if we reached EOF without a '\n' char there.)
*/
void print_list(pListOfStr p)
{
    for( ; p != NULL; p = p->next ) printf( p->str );
}

/* utility function to return a copy ... in NEWly allocated memory */
CString newCopy( char str[] )
{
    CString nCopy = (CString) malloc( strlen(str)+1 );
    myAssert( (nCopy != NULL), "Error: malloc failed to allocate memory (2)" );

    strcpy( nCopy, str );
    return nCopy; /* REMEMBER to *free* this new string when done with it ... */
}


Here is a C safe way to input strings of ANY length from the keyboard ... (that you could use in the above) ...
(Note: but may need to update to check for fail to allocate memory ...)

Code: [Select]
/* c_safe_getline.c */ /* this version 2010-09-07 */

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

/* safe C line input from keyboard ... SAFE for ANY line LENGTH */
/* REMEMBER to free the new memory ... when done with it ...  */
char* c_safe_getline()
{
    int c, buf_len = 256, len = 0;
    char* buffer = (char*) malloc( buf_len ); /* good to check if memory allocated */

    /* eats up WHOLE line ... including '\n' */
    while( (c = getchar()) != '\n' )
    {
        if( len == buf_len-1 ) /* good up t0 value buf_len-2*/
        {
            buf_len += buf_len;
            buffer = (char*) realloc( buffer, buf_len ); /* good to check if allocated ok ...*/
        }
        buffer[len++] = c;
    }

    buffer[len] = 0; /* confirm NULL terminated ... */
    return (char*) realloc( buffer, len+1  ); /* but ... good to check if allocated ok */
}

char* get_line( char prompt[] )
{
    printf( prompt );
    return c_safe_getline();
}

/*
    defaults to 'yes' ... i.e. user must enter 'n' or 'N' for 'No'
    returns '1' for 'Yes' or '0' for 'No' ...
*/
int more()
{
    int c, reply;

    printf( "\nMore ... y/n ? " );
    reply = c = getchar();
    while( c != '\n' )
        c = getchar(); /* flush stdin ... */

    if( reply == 'n' || reply == 'N' )
        return 0; /* No more ... */

    /* else ...*/
    return 1; /* Yes more ... */
}


int main() /* to test 'char* c_safe_getline()' */
{
    char* s;
    do
    {
        s = get_line( "Enter line: " );
        printf( "You entered: '%s'\n", s );
        free( s );

    }while( more() );

    return 0;
}


And ... using the c_safe_getline() ... in the insert sort list example above ...

Code: [Select]
/* ListOfStr2.c */ /* this version 2010-09-07 */

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

typedef struct ListOfStr
{
    char* str; /* 'str' holds the address of a new C string ... */
    struct ListOfStr* next; /* points to the next C string ... */
} ListOfStr ;

typedef ListOfStr* pListOfStr; /* holds base address of a list of C strings */


/* prototypes ... */

/* INSERTS new C string 'data' into 'list' in 'ABC...abc...' order */
pListOfStr insert_sorted( char* data, pListOfStr list );

/* erase/free up all memory in list */
pListOfStr erase_all( pListOfStr list );

/* show all the (insert_sorted-sorted) 'text lines' in the 'list' */
void print_all( pListOfStr p );

/* utility function to return line in NEWly allocated memory */
char* c_safe_getline();

int strcmpIgnoreCase( char*, char* );

void myAssert( int condition, char text[] )
{
    if( !condition )
    {
        fprintf( stderr, "%s.  Press 'Enter' to exit ... ", text );
        getchar();
        exit(1);
    }
}



int main() /* * * * * * * * * * * * * * MAIN BEGINS * * * * * * * * * * * * * */
{
    /* in the following ... list is decalred as a pointer to ListOfStr ... */
    pListOfStr list = NULL; /* 'list' ... holds the address of the list's HEAD */

    char* line;
    char* prompt = "Enter a line of text (or just press 'Enter' to end) : ";

    /* just press 'Enter' ... and no text entered to end this loop ... */
    while( printf( prompt ) && (line = c_safe_getline()) && line[0] != 0 )
        list = insert_sorted( line, list );

    free( line ); /* free last line's allocated memory when line[0] == 0 */

    print_all( list );

    list = erase_all( list ); /* ... and re-sets list to NULL ... */

    printf( "\nPress 'Enter' ... to continue ... " );
    getchar();
    return 0;

}/* * * * * * * * * * * * * * * * * * MAIN ENDS * * * * * * * * * * * * * * * */



pListOfStr insert_sorted( char* data, pListOfStr list )
{
    /* create a new node */
    pListOfStr p = (pListOfStr) malloc( sizeof(ListOfStr) );
    myAssert( (p != NULL), "Error: malloc failed to allocate memory" );

    /* save address of new memory into the new node ... */
    p->str = data ;

    /* firstly, handle the frequent case where 'data' is NOT the first element */
    if( list != NULL && strcmpIgnoreCase(data, list->str) >= 0 )
    {
        pListOfStr q = list;
        while( q->next != NULL && strcmpIgnoreCase(q->next->str, data) <= 0 )
            q = q->next;

        p->next = q->next; /* N->B*/
        q->next = p; /* A->N */
        return list;
    }
    /* else ... */
    /* if program reaches here, search the linked list for the right location */
    /* yes ... this IS the first element */
    /* so set to be the first element */
    p->next = list;
    return p;
}

pListOfStr erase_all( pListOfStr list )
{
    pListOfStr p;
    while( list != NULL )
    {
        p = list->next;
        free( list->str );
        free( list );
        list = p;
    }
    return list; /* i.e. return NULL; */
}

void print_all( pListOfStr p )
{
    for( ; p != NULL; p = p->next ) printf( "%s\n", p->str );
}

/* utility function to return line in NEWly allocated memory */
char* c_safe_getline()
{
    int c, buf_len = 256, len = 0;
    char* tmp, * line = (char*) malloc( buf_len );
    myAssert( (line!=NULL), "Error: malloc failed to allocate memory" );

    /* eats up WHOLE line ... including '\n' */
    while( (c = getchar()) != '\n' )
    {
        if( len == buf_len-1 ) /* good upto buf_len-2 */
        {
            buf_len += buf_len;
            tmp = (char*) realloc( line, buf_len );
            if( tmp == NULL )
            {
                free( line );
                myAssert( 0, "Error: realloc failed to allocate memory" );
            }
            /* if reach here ... good realloc ... so update ... */
            line = tmp;
        }
        line[len++] = c;
    }

    line[len] = 0; /* confirm NULL terminated ... */
    tmp = (char*) realloc( line, len+1  );
    if( tmp == NULL )
    {
        free( line );
        myAssert( 0, "Error: realloc failed to allocate memory" );
    }
    return tmp;
}

int strcmpIgnoreCase( char* a, char* b )
{
    while( *a != 0 && *b != 0 && tolower(*a) == tolower(*b) ) ++a, ++b;
    /* now examine end  condition ...*/
    return( tolower(*a) - tolower(*b) );
}
Title: Re: New thread especially for students of C and C++
Post by: David on April 19, 2009, 08:13:02 AM
And ... a little about linked lists for C++ students who may not yet C ...

A mini C++ linked lists tutorial ... (taken from my recent comment's posted at ...)

1st link ...
http://www.dreamincode.net/forums/showtopic98767.htm

2nd link ...
http://www.dreamincode.net/forums/showtopic99329.htm

Or ... try this first, maybe ...
http://www.dreamincode.net/forums/index.php?s=&showtopic=152343&view=findpost&p=908336

From the first link ... you will see below ...

1. a simple Node (with just one int value) ...
2. with an insert sort demo'd ...
3. and some discussion re. pointers ...
4. also a Node(with an int value and a string value)
5. passing by value and returning a function value
6. passing/returning by ref
7. typedef Node* pNode discussed and demo'd

A Linked-List is quite a different data structure than an array ...

If data item 'a' has Address A, data item 'b' has address B, etc ...

Then the start address is ... (the value in the pointer variable pHead is) ... A
and (the node at) A points to B
and (the node at) B points to C
...
...
...
and Z (i.e. the last node/element) points to a NULL terminal value.

pHead = A; // where A is the start address that was assigned by 'new'


This simple working example may help you get started ...

Code: [Select]
// linked_list_ex1.cpp // rev 2010-07-25

#include <iostream>
using namespace std;

struct Node
{
    int data;
    Node* next;
};

// after this following typedef statement ...
// where-ever you would have had (type) Node*
// you may now more simply use (type)  pNode

typedef Node* pNode;

// Node* take_in_more( Node* pHead)
pNode take_in_more( pNode pHead )
{
    // recall pNode is type pointer to a Node, i.e. Node*
   
    // get pEnd ... the address of the last node in the list
    pNode pEnd;
    int i = 0; // i holds count of nodes ...
    if( pHead != NULL )
    {
        for( pEnd = pHead; pEnd->next != NULL; pEnd = pEnd->next, ++i )
            ;
    }
    // now ... i holds count, pEnd holds address of last Node

    char prompt[] = "('x' to eXit) Enter a value for number ";
    // advance i to next count for this new number to be input ...
    while( cout << prompt << ++i << ": " && cin >> num )
    {
        pNode pNew = new Node; // get new memory to hold a Node
        pNew->data = num; // fill in values for this Node
        pNew->next = NULL;
       
        if( pHead != NULL ) // append (for all passes after the first pass)
        {
            pEnd->next = pNew; // link to new node
            pEnd = pNew; // reset 'pEnd' to this latest new node
        }
        else // pHead is set on the first pass only ...
            pHead = pEnd = pNew;
    }
    cin.clear(); // clear cin error flags ...
    cin.sync(); // 'flush' cin stream ...
    return pHead;
}

// show all the node datas ...
// note: pStart is a local copy of the pointer ( the address )
void show_all( pNode pStart )
{
    int i = 0;
    while( pStart != NULL )
    {
        cout << "node " << ++i << " data : "
             << (pStart->data) << endl;
        pStart = pStart->next;
    }
}

void delete_all( pNode& pStart )
{
    while( pStart != NULL )
    {
        pNode pCopy = pStart; // copy address in pStart into pCopy ...
        pStart = pStart->next; // advance to the next address ... (it may be NULL)
        delete( pCopy ); // free ... the node at this address
    }
    // Note: When done the above loop ... pStart is equal to NULL
    // So ... via 'pass by ref' ... pStart (i.e. pHead) is set to NULL
}


int main()
{
    // Node* pHead = NULL; // Note: pHead must be NULL to start
    pNode pHead = NULL; // Note: pHead must be NULL to start

    cout << "Enter some numbers ... \n\n";
    pHead = take_in_more( pHead );
    cout << "\nSo far ...\n";
    show_all( pHead );

    cout << "\nEnter some more numbers ...\n\n";
    pHead = take_in_more( pHead );
    cout << "\nWith added ...\n";
    show_all( pHead );

    delete_all( pHead );
    cout << "\nAfter delete_all ...\n";
    show_all( pHead );

    cout << "\nNow again, enter some numbers ...\n";
    pHead = take_in_more( pHead );
    cout << "\nList at present ...\n";
    show_all( pHead );
    delete_all( pHead );

    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get(); // wait for 'Enter' key to be pressed ...
}


And ... after you've grasped the pointer style used in linked-lists ... here is an insert-sort example that inserts each new element, as it is entered, in order, in a list ...

Code: [Select]
// linked_list_ex2.cpp // rev 2010-07-25

#include <iostream>
#include <string>

// re. atoi converting an 'alpha' C string 'to' an 'int'
#include <cstdlib>

using namespace std;

struct Node
{
    string name;
    int id;
    Node* next;
};

// Note: variable type pNode if for holding an address of a 'Node'
typedef Node* pNode;


// insert each new node in the list with names in proper order
pNode insert_sorted( pNode pHead, pNode pNew )
{
    // First ... handle case where new node should be first element
    if( pHead == NULL || pNew->name < pHead->name )
    {
        // It now becomes the first element
        pNew->next = pHead; // old pHead becomes 2nd in list
        pHead = pNew; // and this new node becomes the head of the list
    }
    else // If here ... search the linked list for the right location
    {
        pNode q = pHead; // Get a working copy of pHead in q
        // Start comparing with element AFTER 'q' ... i.e. the 'next in'
        while( q->next != NULL && q->next->name <= pNew->name )
        {
            q = q->next; // Traverse the list ...
        }

        // Ok, insert after 'q' by relinking the pointers
        // ( Includes inserting at end position ... where q->next == NULL )
        // for A->B to became A->N->B ... first insert N->B
        pNew->next = q->next; // 'N' is first linked to 'B'
        // and now A->N
        q->next = pNew; // now 'A' is updated to link to 'N'
    }

    return pHead; // since it may have changed, return update
}

pNode take_in_more( pNode pHead )
{
    char prompt[] = "(Blank line to exit) ... Enter name: ";
    char prompt2[] = "Enter ID number: ";
    string tmpStr;
    while( cout << prompt && getline( cin, tmpStr ) )
    {
        if( tmpStr == "" ) break;
       
        pNode pTmp = new Node;
        pTmp->name = tmpStr;
        cout << prompt2;
        getline( cin, tmpStr );
        pTmp->id = atoi( tmpStr.c_str() );

        // Note: the address held in pHead may be updated during the 'insert'
        pHead = insert_sorted( pHead, pTmp ); // Note: HERE we insert each Node
    }

    return pHead;
}

// Show all the node data ...
// Note: pStart is a local copy of the pointer (the address)
void show_all( pNode pStart )
{
    for( int i = 0; pStart != NULL; pStart = pStart->next )
        cout << "Node " << ++i
             << " with id: " << pStart->id
             << " name : " << pStart->name
             << endl;
}

void delete_all( pNode& pStart )
{
    while( pStart != NULL )
    {
        pNode pCopy = pStart; // copy address in pStart into pCopy ...
        pStart = pStart->next; // advance to the next address ... (it may be NULL)
        delete( pCopy ); // free ... the node at this address
    }
    // Note: When done the above loop ... pStart is equal to NULL
    // So ... via 'pass by ref' ... pStart (i.e. pHead) is set to NULL
}


int main()
{
    cout << "Enter some data ... \n\n";

    pNode pHead = NULL; // Note: pHead must be NULL to start
    pHead = take_in_more( pHead );
   
    cout << "\nSo far ...\n";
    show_all( pHead );

    cout << "\nEnter some more data ...\n\n";
    pHead = take_in_more( pHead );
   
    cout << "\nWith added ...\n";
    show_all( pHead );

    delete_all( pHead );
   
    cout << "\nAfter delete_all ...\n";
    show_all( pHead );

    cout << "\nNow again, enter some data ...\n";
    pHead = take_in_more( pHead );
   
    cout << "\nList at present ...\n";
    show_all( pHead );
   
    delete_all( pHead );
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get(); // wait for 'Enter' key to be pressed ...
}


Pointers are just variables to hold an address ... And getting the bits, at different addresses, (of various memory chunks in RAM), and interpreting that bit pattern ...

is it data? (if data ... what decoding to use?) ...
is it an instruction? (ok ... execute that instruction) ...

is really what computing is all about ...

Code: [Select]
// you can refer to a value ... directly ... by its variable name

int number = 5;
cout << number << endl; // prints 5

// or ... indirectly ... by its address ...

// Note: pNumber is declared as a variable to hold the address of an 'int'
int* pNumber = &number; // or ... int* pNumber; pNumber = &number;
cout << *pNumber << endl; // prints 5


This re-edited program below, (with more comments), may help some more ...

Code: [Select]
// linked_list_ex3.cpp // rev 2010-07-25

#include <iostream>

using namespace std;

struct Node
{
    int data;
   
    // Note: 'next' is used to hold the address
    // of the 'next' Node in the linked-list
    Node* next;
};

// Note: pNode is here 'defined' as ...
// a variable type to hold an address of a 'Node'
typedef Node* pNode;

// Note: pHead gets updated (in main) by 'pass by ref'
void take_in_more( pNode& pStart )
{
    // Here ... we want to ...
    // set pEnd to address of the last node in the list,
    // set i to number of nodes
    pNode pEnd;
    int i = 0;
    if( pStart != NULL )
    {
        for( pEnd = pStart; pEnd->next != NULL; pEnd = pEnd->next, ++i )
            ;
    }

    char prompt[] = "('x' to eXit) Enter number ";
    int num;
    // advance i to show number of next new integer to be input ...
    while( cout << prompt << ++i << ": " && cin >> num )
    {
        pNode pNew = new Node; // get new memory to hold a new 'int' and a new 'address'
        pNew->data = num; // Ok ... copy 'num' to this new memory to hold an 'int'
        pNew->next = NULL; // don't forget to 'NULL terminate' each new 'end' Node

        if( pStart != NULL ) // append ... for all passes with a list of one or more elements
        {
            pEnd->next = pNew; // link 'end' Node to new node (after it now)
            pEnd = pNew; // reset 'pEnd' to hold address of this latest new node
        }
        else // pStart (and pHead by ref.) get set here ... *ONLY* if the list passed in was empty
            pStart = pEnd = pNew; // pStart and pEnd now hold this address (i.e. NOT NULL now)
    }
    cin.clear(); // clear cin error flags caused by 'x' instead of an int
    cin.sync(); // 'flush' cin stream ...
}

// show all the node data ...
// Note: pStart is a local copy of the pointer (the address)
void show_all( pNode pStart )
{
    for( int i = 0; pStart != NULL; pStart = pStart->next  )
        cout << "node " << ++i
             << " data : " << (pStart->data)
             << endl;
}

void delete_all( pNode& pStart )
{
    while( pStart!=NULL )
    {
        pNode pCopy = pStart; // copy address in pStart into pCopy ...
        pStart = pStart->next; // advance to the next address (it may be NULL)
        delete( pCopy ); // free the node memory at this address
    }
   
    // Note: When done the above loop, pStart is equal to NULL
    // So, via 'pass by ref' ... pStart (i.e. pHead) is set to NULL
}



int main()
{
    // Note: pHead must be NULL to start
    pNode pHead = NULL;

    cout << "Enter some numbers ... \n\n";
    take_in_more( pHead );
    cout << "\nSo far ...\n";
    show_all( pHead );

    cout << "\nEnter some more numbers ...\n\n";
    take_in_more( pHead );
    cout << "\nWith added ...\n";
    show_all( pHead );

    delete_all( pHead );
    cout << "\nAfter delete_all ...\n";
    show_all( pHead );

    cout << "\nNow again, enter some numbers ...\n";
    take_in_more( pHead );
    cout << "\nList at present ...\n";
    show_all( pHead );
   
    delete_all( pHead );
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get(); // wait for 'Enter' key to be pressed ...
}


C++ linked-list intro ... continued below ...
Title: Re: New thread especially for students of C and C++
Post by: David on April 19, 2009, 08:36:58 AM
Intro to linked-lists, using C++, continued from above ...

The pass by ref syntax used here may make the code a little simpler to follow ...

Code: [Select]
// linked_list_ex4.cpp // rev 2010-07-25

#include <iostream>
#include <fstream> // ostream ... and overloaded operator <<
#include <string>
#include <cstdlib> // re. atoi ... 'alpha to int'

using namespace std;

struct Node
{
    string name;
    int id;
    Node* next; // to hold the address of the 'next' Node
};

// Note: pNode is 'defined' as ...
// a variable type to hold an address of a 'Node'
typedef Node* pNode;

// insert each new node in the list with names in proper order
// Note: pStart (pFront in calling function) is updated by 'pass by ref'
void insert_sorted( pNode& pStart, pNode pNew )
{
    // Firstly, handle the most common case, where
    // the New node is NOT the first element ...
    if( pStart != NULL && pNew->name >= pStart->name )
    {
        pNode q = pStart; // Get a working copy of pStart in q
        // Start comparing with element AFTER 'q' ... i.e. the 'next in'
        while( q->next != NULL && q->next->name <= pNew->name )
        {
            q = q->next; // Traverse the list ...
        }

        // Ok, insert after 'q' by relinking the pointers
        // ( Includes inserting at end position ...
        // ... where q->next == NULL )
        // for A->B to become A->N->B
        // first link N->B
        pNew->next = q->next; // since q->next is address of 'B'
        // and now A->N
        q->next = pNew; // since pNew is address of 'N'
    }
    else // It now becomes the first element ...
    {   // Note: pStart may have been NULL ... that's 'no problem'
        pNew->next = pStart; // old pStart becomes 2nd in list
        pStart = pNew; // and this new node becomes the start of the list
    }
}

// Note: pFront (pHead in main) is updated by 'pass by ref'
void take_in_data( pNode& pFront )
{
    char prompt[] = "(Enter a blank line to exit) ... Enter name: ";
    char prompt2[] = "Enter ID number: ";
    string tmpStr;
    while( cout << prompt && getline( cin, tmpStr ) )
    {
        if( tmpStr == "" ) break;
       
        pNode pNew = new Node;
        pNew->name = tmpStr;
       
        cout << prompt2;
        getline( cin, tmpStr );
        pNew->id = atoi( tmpStr.c_str() );

        // Note: the address held in pFront may be updated during the 'insert'
        // since we are using 'pass by ref' for 'pFront' ...
        insert_sorted( pFront, pNew ); // Note: HERE we insert each Node
    }
}

// show all the node data ...
// note: pStart is a local copy of the pointer ( i.e. the address) passed to it
void show_all( pNode pStart )
{
    for( int i = 0; pStart != NULL; pStart = pStart->next )
        cout << "node " << ++i << " with id: " << pStart->id
             << " name : " << pStart->name << endl;
}

// Or ... can show all data via:  cout << pStart;
// via an overloaded operator <<
// Here's how ... Note! ostream object is passed in AND returned 'by ref'
// Note 2: This function HAS 2 passed in variables (objects)
ostream& operator << ( ostream& fout, pNode pStart )
{
    for( int i = 0; pStart != NULL; pStart = pStart->next )
        fout << "node " << ++i << " with id: " << pStart->id
             << " name : " << pStart->name << endl;
    return fout;
}

// Note: pStart (pHead in main) is updated by 'pass by ref'
void delete_all( pNode& pStart )
{
    while( pStart != NULL )
    {
        pNode pCopy = pStart; // copy address in pStart into pCopy
        pStart = pStart->next; // advance to the next address (it may be NULL)
        delete( pCopy ); // free the node memory at this address
    }
   
    // Note: When done the above loop ... pStart is equal to NULL
    // So via 'pass by ref' ... pStart (i.e. pHead in main) is set to NULL
}




int main()
{
    // Note: pHead MUST be NULL to start ...
    pNode pHead = NULL;

    cout << "Enter some data ... \n\n";
    take_in_data( pHead );
    cout << "\nSo far ...\n";
    show_all( pHead );

    cout << "\nEnter some more data ...\n\n";
    take_in_data( pHead );
    cout << "\nWith added ...\n";
    show_all( pHead );

    delete_all( pHead );
    cout << "\nAfter delete_all ...\n";
    show_all( pHead );

    cout << "\nNow again, enter some data ...\n";
    take_in_data( pHead );
    cout << "\nList at present ...\n";
    cout << pHead; // using overloaded operator <<

    delete_all( pHead );
    cout << "\nPress 'Enter' to exit ... " << flush;
    cin.get(); // wait for 'Enter' key to be pressed ...
}


Quote
... thanks, the only thing I'm unsure of ... is  ... What is a node?

Like ... I know the '*' is to point to what ever is in that variable ...


Not really ... * is used to indicate a variable is a pointer variable

int* p; // indicates that p is a variable to hold the address of an integer


Or ... * is used to de-reference an address, i.e. to take/use the value at an address ...

*p = 7; // means to assign/copy 7 to the memory beginning at the address in the (int pointer) variable p
           // BUT this WILL CRASH if you haven't a valid address in p of memory reserved to hold an int,
           // as we don't yet ... here ... but see the example program snippet below for a proper way to do it.

int seven = *p; // will assign/copy into the memory reserved for the variable seven ... the value 7

int* q = NULL; // declare q as an int pointer variable and assign it the initial value NULL
q = & seven;   // will copy the address of the memory reserved for 'int seven' into q ...
                    // so that q now holds a valid address and NOT NULL any more

cout << *q << endl; // will print out the value at the address in q, which is 7
cout << hex << q << endl; // will print out the hexidecimal value of the address held in the variable q
cout << hex << &q << endl; // will print out the hexidecimal value of the address OF the variable q


Code: [Select]
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    int* p; // indicates that p is a variable to hold the address of an integer

    // get some new memory to hold an integer ... (4 bytes in many systems today)
    p = new int; // put the address of (the start byte of) that memory into p

    // Or ... * is used to de-reference an address,
    // i.e. to take/use the value at an address ...
    // here we assign/copy 7 to the address held in the (int pointer) variable p

    *p = 7;
    cout << *p << endl;

    // assign/copy into memory reserved for the variable seven ... the value 7
    int seven = *p;

    // declare q as an int pointer variable and assign it the initial value NULL
    int* q = NULL;

    // copy the address of the memory reserved for 'int seven' into q ...
    //so that q now holds a valid address and NOT NULL any more
    q = & seven;

    // print out the value at the address in q, which is 7
    cout << *q << endl;

    // print out the hexidecimal value of the address held in the variable q
    cout << hex << q << endl;
    cout << hex << &seven << endl; //and the address of the memory used by seven
    
    // print out the hexidecimal value of the address OF the variables q and p
    cout << hex << &q << endl;
    cout << hex << &p << endl;
    

    cin.get();
}
// My output from running the compiled program above was ...
/*
7
7
0x23ff70
0x23ff70
0x23ff6c
0x23ff74
*/

Quote
but I've never used 'typedef Node* pNode' before.
However ... I guess this is good though ... because this one program is making me learn more than I've learned all semester.

Oh, and David ... when I stated the assignment, I guess I worded it poorly ... I don't really need to store the random numbers into an array first.  I could just have sorted the list, as it is made, via insert sort. But for simplicity ... I stored it into an array so I could see the numbers ... and thus know what was going on. (Note: Since I didn't use srand the numbers aren't truly random ... rather ... the same set of random numbers appears every time.)

Programming can be fun when you start to see what is going on and begin to enter into the problem solving process. The ability to, via a PC, quickly try out some well designed code for a well defined task and see it actually do what it was designed to do ... you may now understand something of the joy of a great creator. (The big historical danger here though, has been ... that we become so enamoured with our little creations that we ignore our own so much more Awesome Creator.)

The word Node is somewhat arbitrary ... you could use List, MyList, MyLinkedList ... or what ever might best describe all that is there' ...

But the word Node ... ( common practice is to put cap's on the first letter of structs or classes you define/create ) ... is very commonly used because it seems to well represent that idea that ONE MORE set of data, often called a data record, or in C/C++, a struct, is being discussed. The special data item included in these node or list structs ( beside any other data items ) ... is a pointer to the next node (next data record) ... i.e. each record/struct ALSO has a variable to hold the address of the next link in the chain.

Array's are often implemented as ( equal sized ) chunks of contiguous memory ... and so the address of the next element in an array can be calculated from the address of the first element ... addressFirst + ( n-1 ) * ( sizeof( each_element ) )

Thus the array[ i ] notation ... where i = 0 .. ( n-1 )

BUT ... each node in a linked list will be at the address that it was given when it was created ( in C++, via the new operator ) ... And the next node may NOT be contiguous. So ... it CAN-NOT be calculated ( from the address of the first node ) . Thus the address of each node must be stored in the pointer variable of the previous node ... a variable often named next

The memory address of the first node, (the first node is often called head or front or start ) , must be stored in a pointer variable ... like pStart ... so we can reach there ... to start.

And for the end node in a linked list ... the value in its pointer variable 'next' will be NULL ... to signal we have reached the end of the chain.

Before we have any data records or nodes ... the value in pHead is assigned NULL to signal that the list is empty.

Of course ... all the above is with respect only to the most primitive linked list ... a list that links in the forward direction only. There are many embellishments ... but this is the common simple place to start.

If you look over the examples here ... you may have noticed 2 styles:

pass/return by ref
and
pass by value and return a function value

and that both styles were alternately used.

Take your choice here.

If/when you get to C++ classes ... you may find the void function pass/return by ref style closer to what you need ... when you want to update the private class data members ... in the public member functions (that have access to all the private data as if they were global variables )

You may find that you now have all the concepts and snippets that you need to put together a suitable solution to your problem.

I applaud your approach in first attempting to get some working code for the much simpler problem of just getting some random numbers into an array of numbers and displaying them.


And from link 2  ...
http://www.dreamincode.net/forums/showtopic99329.htm

Quote
My code is supposed to give three situations...

1. If user inputs 1, then code has to list the student name and id already in the code.

2. If user inputs 2, then the code should allow the user to add a student name and id to the linked list
   to be automatically inserted as (previous id+1) ...

3. If user inputs 3, response should be to exit the program ...

(However, my program, even though it compiles, doesn't work properly ...

... to keep it simple, the program below may give you some ideas and a working start:

Note 1: this list keeps track of pTail, (as well as pHead), to facilitate appending a node (i.e. push_back)
Note 2: typedef is great if one can read a program 'top down' ...

Programming is much about defining new things,

like ...
  
  functions,
  classes,
  types,
  structs,
  #include "helper.h",
  etc ...

to aid smooth program logic flow ...

And whatever helps to make a program flow more smoothly ... including good type names ...  is highly recommended ... yes/no?

Code: [Select]
// linked_list_ex5.cpp // rev 2010-07-25

#include <iostream>
#include <string>

using namespace std;

struct Node
{
    string name;
    int id;
    Node *next;
};

typedef Node* pNode;

void push_back( pNode& head, pNode& tail, int new_id, const string& new_name )
{
    pNode pNew = new Node;
    pNew->id = new_id;
    pNew->name = new_name;
    pNew->next = NULL;

    if( head != NULL )
    {
        tail->next = pNew;
        tail = pNew;
    }
    else
        head = tail = pNew;
}

void push_front( pNode& head, pNode& tail, int new_id, const string& new_name )
{
    pNode pNew = new Node;
    pNew->id = new_id;
    pNew->name = new_name;

    pNew->next = head;
    head = pNew;
   
    if( tail == NULL )
        tail = pNew;
}

void display_all( pNode front )
{
    if( front == NULL)
    {
        cout << "The list is empty.\n";
        return;
    }
   
    int i = 0;
    while( front )
    {
        cout << "Node[" << ++i << "] is holding "
             << "ID[" << front->id << "] "
             << front->name << endl;
        front = front->next;
    }
}

void delete_all( pNode& front, pNode& back )
{
    while( front )
    {
        pNode copy  = front;
        front = front->next; // Note: when done ... front = NULL
        delete( copy ); // ok ... delete the memory 'pointed to' ...
    }
    back = NULL; // these new values are returned 'by ref'
}



int main()
{
    // get some name data ...
    const char* names[] = { "Susan", "Sarah", "Karry", "Joe" };
    const int num_names = sizeof names / sizeof names[0] ;
   
    pNode front = NULL, tail = NULL; //
   
    int id ;
    cout << "After push_back loop ... \n";
    for( id = 0; id < num_names; ++id )
        push_back( front, tail, id+1, names[id] );

    display_all( front);
    cout << endl;


    cout << "After push_back of 'visitor' ... \n";
    // note id has value num_names now ... at end of above loop
    push_back( front, tail, id+1, "Guest visitor" );
    display_all( front );
    cout << endl;

    cout << "After delete_all ... then push_front loop ... \n";
   
    delete_all( front, tail );
    for( id = 0; id < num_names; ++id )
        push_front( front, tail, ++id, names[id] );
    display_all( front );
    cout << endl;
   
    cout << "After delete_all ... \n";
   
    delete_all( front, tail );
    display_all( front );
    cout << "\nPress 'Enter' to continue ... " << flush;
    cin.get();
}

// Program output ...
/*
After push_back loop ...
Node[1] is holding ID[1] Susan
Node[2] is holding ID[2] Sarah
Node[3] is holding ID[3] Karry
Node[4] is holding ID[4] Joe

After push_back of 'visitor' ...
Node[1] is holding ID[1] Susan
Node[2] is holding ID[2] Sarah
Node[3] is holding ID[3] Karry
Node[4] is holding ID[4] Joe
Node[5] is holding ID[5] Guest visitor

After delete_all ... then push_front loop ...
Node[1] is holding ID[4] Joe
Node[2] is holding ID[3] Karry
Node[3] is holding ID[2] Sarah
Node[4] is holding ID[1] Susan

After delete_all ...
The list is empty.

Press 'Enter' to continue ...
*/

And you can now jump to the next link to see an added upgrade of the above  ... using a C++ class ...

class MyStudentList

http://developers-heaven.net/forum/index.php/topic,106.msg632.html#msg632

Note the encapsulation and data hiding ... especially to the pointers ...

Using this C++ class version ... you may not notice in your coding (outside the class) - for example, in the  main function - that pointers were even involved in using a list.
Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2009, 12:09:03 PM
Carrying on with C++ lists ...this may be a useful example for C++ students

class Student using the STL list container to hold class Student objects ...

Code: [Select]
// this revision 2010-07-25 ...

// A simple example of Student records ...
// using a C++ 'list' container to hold each 'Student' element

// 1. adds info from keyboard into a list of 'Student' records (using push_back)
// 2. shows the 'records' in memory on the screen
// 3. writes to file ...         all the records in memory
// 4. reads from file and stores all the records in memory (in a C++ list)
// 5. allows edit/erase ... to a Student record
// 6. sorts Student records by student name ...
// 7. sorts Student records by student id number ...

// Note:
// * this program allows only unique student id's for each record
// * this program is set to update its file, (if there were any changes),
//   before quitting

#include <fstream>
#include <iostream>
#include <string>
#include <list>
#include <cctype>

// Globals ...
using namespace std;

// file name of file for term's records ...
const char THIS_TERM[] = "Fall2010.txt";

const string MENU =
"\nWhat do you want to do ?\n\n"
"\t1. A dd a new student name and id ? \n"
"\t2. E dit/E rase a student record ? \n"
"\t3. S ort student records ? \n"
"\t4. V iew all in memory at present ? \n"
"\t5. U pdate file with data currently in memory ? \n"
"\t6. L oad in data from file ? \n"
"\t7. e X it program ? ";

class Student
{
public:
    // constructors ...
    Student() {}
    Student( string nName, string nId ) : name(nName), id(nId) {}

    // setters ...
    void setName( string nName ) { name = nName; }
    void setID( string nId ) { id = nId; }

    // getters ...
    string getName() { return name; }
    string getID() { return id; }
private:
    string name, // add any needed info here, just like in a C/C++ 'struct'
           id;
};


// functions used by main to process a list of Student records

// returns a valid iterator if ID is used already ... otherwise returns 0
list< Student >::iterator existID( list< Student >& term, const string& ID )
{
    list< Student >::iterator it;
    for( it = term.begin(); it != term.end(); ++it )
    {
        if( it->getID() == ID )
            return it;
    }

    return term.end();
}

// adds Student records to end of end of list of Student records ... 'term'
// gets input from keyboard ...
int newStud( list< Student >& term )
{
    cout << "\nEnter an empty record to exit this 'Input Loop' ..." << endl;
    int count = 0, reply;
    string nam, num;
    for( ;; ) // loop forever until break ...
    {
        cout << "\nID   : ";
        getline(cin, num);
        if( existID( term, num ) != term.end() )
        {
            cout << "\nThat 'id' " << num << " already exits ... " << endl;
            continue; // from the top of the forever loop right now
        }
        cout << "Name : ";
        getline(cin, nam);
        if( nam=="" || num=="")
            break;

        cout << "Add or Redo (a/r) ? ";
        reply=cin.get();
        cin.sync();
        if ( toupper(reply)!='A' )
        {
            cout << "Aborted ..." << endl;
            continue;
        }

        // ok ... create and add this record to the end of the list ...
        term.push_back( Student(nam, num) );
        ++count;
        cout << "Added ..." << endl;
    }
    return count;
}

// shows (to console screen) all student records in list container ... 'term'
void viewStuds( list< Student >& term )
{
    list< Student >::iterator it;
    int i = 0;
    for( it = term.begin(); it != term.end(); ++it )
    {
        cout << ++i << ".\n"
             << "Name   : " << it->getName() << endl
             << "Number : " << it->getID() << endl;
    }
}

// file all records in memory ... create/overwrite file with name 'THIS_TERM'
int fileStuds( list< Student >& term )
{
    ofstream fout( THIS_TERM );  // recall 'THIS_TERM' is a Global variable
    if ( !fout )
    {
        cout << "\nError attempting to open file ..." << endl;
        return -1;    // report error condition ...
    }

    // else ...
    list< Student >::iterator it;
    int i = 0;
    for( it = term.begin(); it != term.end(); ++it )
    {
        fout << it->getName() << "," << it->getID() << endl;
        ++i;
    }

    fout.close();

    if( i == (int)term.size() )
        cout << "\nAll " << i << " records filed ok." << endl;
    else
        cout << "\nOnly " << i << " of " << term.size()
             << " records were written ..." << endl;

    return i; // report success ... i.e. report count of records filed
}

// reads in all Student records from file 'THIS_TERM' ... if it exists
// returns -1 if no file exists; else returns the number of records read
int readStuds( list< Student >& term )
{
    ifstream fin( THIS_TERM ); // recall THIS_TERM is a Global variable
    if ( !fin )
    {
        cout << "Error attempting to open file ... "
             << "(Perhaps it dosen't exist yet?)" << endl;
        return -1;          // report error condition ...
    }

    // else ... check existing term.size() first before re-set
    if( term.size() ) // i.e. if not == 0 ...
    {
        cout << "\nDo you want over-write the " << term.size()
             << " records in memory (y/n) ? " << flush;
        char reply = toupper( cin.get() );
        cin.sync();
        if( reply != 'Y' )
        {
            cout << "Aborted ... " << flush;
            return 0;
        }
        // if reach here ...
        cout << "Ok ... will over-write the " << term.size()
             << " records in memory ... " << flush;
    }

    // if reach here ...
    term.clear(); // list is empty now ...
    string nam, num;
    int i;
    for( i=0; getline( fin, nam, ',' ); ++i ) //first get 1st string (up to ',')
    {
        getline( fin, num, '\n' ); // then get rest of line (up to '\n')
        term.push_back( Student(nam, num) ); // construct and add new Student
    }
    fin.close();
    return i; // report success? ... i.e. return the record count ...
}

// returns 'true' if a record was edited or erased; otherwise returns false
bool editStud( list< Student > &term )
{
    cout << "\nEnter an empty record to exit this 'Edit/Erase Function' ..."
         << "\nEnter the ID of the student record to edit ? " << flush;
    string idStr, nam;
    getline( cin, idStr );

    list< Student >::iterator i, index;
    i =  existID( term, idStr );
    if( i == term.end() )
    {
        cout << "This '" << idStr << "' does not exist." << endl;
        return false;
    }

    // else ... show ... and ask if ok to edit ...
    cout << "Name   : " << i->getName() << endl
         << "Number : " << i->getID() << endl

         << "Ok to edit/erase (y/n) ? " << flush;

    int reply = toupper( cin.get() );
    cin.sync();
    if( reply != 'Y' )
    {
        cout << "Aborted ... " << endl;
        return false;
    }

    cout << "\nDo you want to erase this record (y/n) ? " << flush;
    reply = toupper( cin.get() );
    cin.sync();
    if( reply == 'Y' )
    {
        term.erase( i );
        cout << "Erased ..." << endl;
        return true;
    }


    // else ...

    cout << "\nOk ... will edit then ...\nNew ID   : ";
    getline(cin, idStr);
    index = existID( term, idStr );
    if( index != term.end() && index!= i )
    {
        cout << "\nThat " << idStr << " already exits ... " << endl;
        return false; // exit to menu now ...
    }

    cout << "New Name : ";
    getline(cin, nam);
    if( nam=="" || idStr=="")
        return false;

    cout << "Ok or Redo (o/r) ? ";
    reply=cin.get();
    cin.sync();
    if( toupper(reply)!='O' ) // cap 'O' ... as in Ok
    {
        cout << "Aborted ..." << endl;
        return false;
    }

    // ok ... do edit
    i->setName( nam );
    i->setID( idStr );
    cout << "Edited ..." << endl;
    return true;
}

// name comparison here is NOT case sensitive ...
bool compare_nocaseName(Student& first, Student& second)
{
  unsigned int i=0;
  while( i<first.getName().length() && i<second.getName().length() )
  {
    if( tolower(first.getName()[i]) < tolower(second.getName()[i]) ) return true;
    else if( tolower(first.getName()[i]) > tolower(second.getName()[i]) ) return false;
    ++i;
  }
  if( first.getName().length() < second.getName().length() ) return true;
  else return first.getID() < second.getID();;
}

bool compare_id(Student& first, Student& second)
{
    return first.getID() < second.getID();
}

void pauseForEnter()
{
    cout << "\nPress 'Enter' to continue ... " << flush;
    cin.sync();
    cin.get();
}



int main()
{
    // create a 'fall list' to hold student names and ids for the 'Fall Term'
    // also holds number of records, via 'fall.size()'
    list <Student > fall;

    // now get all records from file ... if it exists ?
    int count = readStuds( fall );
    if( count >= 0 )
        cout << count << " student record(s) read into memory ..." << endl;
    else
        cout <<"(The file will be created when some student records exist.)"
             <<endl;

    bool changes = false; // set 'update file flag' to initial value ...
    int reply;
    for( ;; ) // loop forever ... until break ...
    {
        cout << MENU;
        reply = toupper(cin.get());
        cin.sync(); // flush cin stream ...

        if( reply == '1' || reply == 'A' )
        {
            int numStuds = newStud( fall );
            cout << endl << numStuds << " student record(s) added ..."
                 << " The total number of student records now is "
                 << fall.size() << endl;
            if( numStuds )
                changes = true; // if >0 update bool variable changes
        }
        else if( reply == '2' || reply == 'E' )
        {
            if( editStud( fall ) )
                changes = true;
        }
        else if( reply == '3' || reply == 'S' )
        {
            cout << "\nSort by id or name  (i/n) ? " << flush;
            reply = toupper( cin.get() );
            cin.sync();
            cout << "Now sorted in memory by ";
            if( reply == 'I' )
            {
                fall.sort(compare_id);
                cout << "id. ";
            }
            else
            {
                fall.sort(compare_nocaseName);
                cout << "name. ";
            }
            cout << "Update file (y/n) ? " << flush;
            reply = toupper( cin.get() );
            cin.sync();
            if( reply == 'Y' )
            {
                changes = true;
                cout << "File to be updated ...\n";
            }
        }
        else if( reply == '4' || reply == 'V' )
            viewStuds( fall );
        else if( reply == '5' || reply == 'U' )
        {
            if( !changes )
                cout << "\nNo changes to file ..." << endl;
            else
            {
                cout << "Are you sure you want to update the file (y/n) ? "
                     << flush;
                reply = toupper( cin.get() );
                cin.sync();
                if( reply == 'Y' )
                {
                    if( fileStuds( fall ) != (int)fall.size() )
                        cout << "\nUNEXPECTED ERROR! NOT all records were filed!"
                             << "\nTry again ... If Error persits call IT."
                             << endl;
                    else
                    {
                        changes = false;// update file flag ...
                        cout << "File write operation confirmed." << endl;
                    }
                }
            }
        }
        else if( reply == '6' || reply == 'L' )
        {
            int condition = readStuds( fall );
            if(  condition >= 0 )
            {
                cout << "\nThere were " << condition
                     << " student records read into memory from file." << endl;
                if( condition > 0 ) changes = false;
            }
        }
        else if( reply == '7' || reply == 'X' )
        {
            if( changes ) // then ...
                fileStuds( fall );
            break; // and exit program ...
        }
        else
        {
            cout << "\nThis choice not implemented yet ... " << endl;
        } // end of if( reply == ? ) switch structure ...

    } // end of forever loop ...


    // debug exit routine ...

    ifstream ftest( THIS_TERM );  // recall 'THIS_TERM' is a Global variable
    if ( !ftest )
    {
        cout << "\nError attempting to open file ..." << endl;
        pauseForEnter();
        return -1;    // report error condition ...
    }

    // if reach here ... first ...
    ftest.close();

    // then ... proceed ... to open the file in a system text editor ...

    // if you have a Win OS ... show structure of file
    string tmpSee = " ";
    tmpSee = "notepad" + tmpSee + THIS_TERM;
    system( tmpSee.c_str() );
}

Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2009, 12:13:24 PM
And something similar in C ...

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* re. strlen, strcmp */

/*
    C/C++ students may like to see ...

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

/* Globals ... */

#define header1 "Student GRADES MANAGEMENT SYSTEM"
#define header2 "1. (I)nitialize Student record list in RAM\n" \
                "2. (A)dd i.e. insert in order, by name, a NEW Student record\n" \
                "3. view/(D)elete a Student record\n" \
                "4. view/(E)dit a Student record\n" \
                "5. (S)how all Student records (as inserted BY NAME above)\n" \
                "6. (F)ile all Student records presently in RAM\n" \
                "7. (R)ead all Student records on file into RAM\n" \
                "8. set/change (P)assword\n" \
                "9. e(X)it"

#define fName "Studentinfo.txt"
#define fPW "StudentPW.txt"
#define minPWLen 8
#define maxStringLen 20

#define sortRead 1

typedef struct Student
{
    int id;
    char* last;
    char* first;
    float test1;
    float test2;
    float assignment;
    float labtest;
    float finalexam;
    struct Student* next;
} Student ;

typedef Student* pStudent;

/* can use these global var's to keep function calls much simpler ... */
pStudent phead = NULL;
int numStudents = 0; /* to hold 'the number of Student records in memory' ...*/
int pwOK = 0;        /* to hold 'the password was OK' flag; 0=false & 1=true */
int fileFlag = 0;    /* fileFlag indicates 'the need to update the file' ... */

/* forward declarations */

pStudent pID(int n);
char* getLine( FILE* fp );
/* char* newCopy( char* str ); */
void add();
void insert( pStudent pS );
void flushStdin();
void showAll();
void viewEdit();
void viewDel();
void view(pStudent pS);
void edit(pStudent pS);
void del(pStudent pS);
void delAll();
void init();
void scramble( char s[] );
void unscramble( char s[] );
void writeAllToFile();
void exitNowYes();
int writeFile();
int readFile();
int passWord();
int newPW();
int StudentCmp( pStudent pS1, pStudent pS2 );

int menu()
{
    int c, selection;
    puts( header1 );
    if( numStudents == 1 ) puts( "Presently there is 1 record in RAM.\n" );
    else printf( "Presently there are %d records.\n\n", numStudents );
    puts( header2 );
    printf( "\nPlease enter your selection  : " );
    selection = c = getchar();
    while( c!='\n' ) c=getchar(); /* 'flush' stdin */
    return selection;
}

void myAssert( int condition, char text[] )
{
    if( !condition )
    {
        fprintf( stderr, "%s.  Press 'Enter' to exit ... ", text );
        getchar();
        exit(1);
    }
}


int main() /* main start * * * * * * * * * * * * * * * * * * * * * * * * * * */
{
    pwOK = passWord();
    numStudents = readFile();
    for(;;) /* Loop forever ... until 'return' ... */
    {
        int choice = -1; /*  error flag */
        if( !pwOK )
            pwOK = passWord();
        choice = menu();
        /* printf("You picked %d ... \n", choice ); */
       
        switch( choice )
        {
            case '1': case 'i': case 'I': init(); break; /* out of 'switch' */
            case '2': case 'a': case 'A': add();  break;
            case '3': case 'd': case 'D': viewDel(); break;
            case '4': case 'e': case 'E': viewEdit(); break;
            case '5': case 's': case 'S': showAll(); break;
            case '6': case 'f': case 'F': writeAllToFile(); break;
            case '7': case 'r': case 'R':
            {
                /* If any were read ... will update Global variable phead */
                int readNum = readFile();
                if( readNum > 0 )
                    numStudents = readNum; /* update Global numStudents */
            }
            break;
            case '8': case 'p': case 'P': pwOK = newPW(); break;
            case '9': case 'x': case 'X': exitNowYes(); break;
            default: puts("\nNot implemented yet ...");
        } /* end switch */
    } /* end for ... */
    return 0;
} /* main end * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

Function definitions are on the next page ...
Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2009, 12:15:18 PM
Functions for the above C program are given below ...


Code: [Select]
/* Function definitions ... */

char* getLine( FILE* fp )
{
    int c, i=0;
    char* buffer = (char*) calloc( maxStringLen+1, 1 );
    myAssert( (buffer!=NULL), "Error: calloc failed to allocate memory" );
    buffer[0] = 0; /* NULL terminate 'as we go' ... */

    /* eats up WHOLE line ... including '\n' */
    while( (c=fgetc(fp)) != EOF && c != '\n' )
    {
        if( i<maxStringLen )
        {
            buffer[i] = c;
            ++i;
            buffer[i] = 0;
        }
    }
    buffer = (char*) realloc( buffer, i+1  );
    myAssert( (buffer!=NULL), "Error: realloc failed to allocate memory" );
    return buffer;
}

/*
char* newCopy( char* str )
{
    int i, len=strlen( str );
    //while( str[len]!=0 ) ++len;
    char * nCopy = (char*) malloc( len+1 );
    myAssert( (nCopy!=-NULL), "Error: malloc failed to allocate memory" );
    for( i=0; i<len; ++i ) nCopy[i] = str[i];
    nCopy[len] = 0;
    return nCopy;
}
*/

/* Return a pointer to the Student if ID found in list; else return NULL */
pStudent pID( int n )
{
    pStudent p = phead;
    if( p==NULL )
        return NULL; /* List empty ... so all id numbers are available */

    while( p!=NULL )
    {
        if( p->id == n )
            return p; /* id number entered was already used */

        /* Else ... get next p to compare */
        p = p->next;
    }

    /* If reach here ... then id number not in list so ... */
    return NULL;
}

void flushStdin()
{
    while( getchar() != '\n' );
}

void exitNowYes()
{
    int c, quit;
    printf("\nPress 'X' to eXit  ... ");
    c = quit = getchar();
    while(c != '\n') c=getchar(); /* flush stdin ... */
    if( quit=='x' || quit=='X' )
    {
        if( fileFlag ) writeAllToFile();
        printf("\nPress 'Enter' to exit right now ... ");
        if( (c=getchar()) == '\n' ) exit(0);
        else while(c != '\n') c=getchar(); /* flush stdin ... */
    }
}

void add() /* ... and insert in the proper place in the list. */
{
    pStudent pS;
    int ID = 0;
    printf("Enter ID         : ");
    scanf("%d", &ID); flushStdin();

    if( pID(ID) || ID<=0  ) /* if pointer returned is NOT NULL, id IS used */
    {
        printf("\nid %d is NOT available.\n", ID );
        return; /* Exit to menu right now ... */
    }
    /* If program reaches here, the ID just entered is available to use. */
    pS = (pStudent) malloc(sizeof(Student));
    myAssert( (pS!=NULL), "Error: malloc failed to allocte memory" );
    pS->id = ID;

    printf("Enter Last Name  : ");
    pS->last = getLine(stdin);
    printf("Enter First Name : ");
    pS->first = getLine(stdin);

    printf("Enter Test1      : ");
    scanf( "%f", &pS->test1 );      flushStdin();
    printf("Enter Test2      : ");
    scanf( "%f", &pS->test2 );      flushStdin();
    printf("Enter Assignment : ");
    scanf( "%f", &pS->assignment ); flushStdin();
    printf("Enter Lab Test   : ");
    scanf( "%f", &pS->labtest );    flushStdin();
    printf("Enter Final Exam : ");
    scanf( "%f", &pS->finalexam );  flushStdin();

    /* Set up link to this node */
    insert( pS );
}

/* insert in list with last & first names in proper order */
void insert( pStudent pS )
{
    /* Firstly, we handle the case where 'this' should be the first element. */
    if(phead == NULL || StudentCmp(pS, phead) < 0)
    {
        /* So ... it now becomes the first element ... */
        pS->next = phead; /* old phead becomes 2nd in list ... */
        phead = pS; /* and ... this pS ... becomes the head of the list */
    }
    else /* If here ... search the linked list for the right location ... */
    {
        pStudent q = phead; /* Get a working copy of phead in q */
        /* Start comparing with element AFTER 'q' ... i.e. the 'next in' ... */
        while(q->next != NULL && StudentCmp(q->next, pS) <= 0)
        {
            q = q->next; /* Traverse the list ... */
        }
        /*
            Ok, insert after 'q' by relinking the pointers (similar to above)
            ( Includes inserting at end position ... where q->next == NULL )
        */
        pS->next = q->next; /* Inserted 'pS' is linked to 'upstream element' */
        q->next = pS;  /* Now 'q' is updated to link to the new 'pS' element */
    }

    /* Update global variables. */
    ++numStudents;
    fileFlag = 1;
}

void showAll()
{
    pStudent p = phead;
    int c;

    if (phead==NULL)
    {
        puts("\nNo records in memory at present.") ;
        return;
    }

    /* If reach here ... */
    
    while( p!=NULL )
    {
        view( p );
        printf("\nPress 'Enter' to continue ... or enter 'A' to abort ... ");
        if( (c=getchar()) == 'a' || c == 'A' )
        {
            flushStdin();
            return;
        }
        while(c != '\n') c=getchar(); /* flush stdin ... */
        p = p->next;
    }
}

void viewEdit()
{
    int yes = 0, ID = 0;
    pStudent p;

    printf("Enter the id number to View/Edit : ");
    scanf("%d",&ID); flushStdin();

    p = pID(ID); /* See if pointer exists; i.e. get value or NULL */
    if( p )     /* i.e. if pointer returned above was not NULL ... */
    {
        view(p);
        printf("\nEdit (y/n) ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' )
        {
            while( yes!='\n' ) yes=getchar(); /* flush stdin */
            edit(p);
        }
        else while( yes!='\n' ) yes=getchar(); /* flush stdin */
    }
    else printf("\nStudent with ID number %d not found.\n", ID);
}

void view(pStudent pS)
{
    printf
    (
        "ID number  : %d\n"
        "Last Name  : %s\n"
        "First Name : %s\n"
        "Test 1     : %.2f\n"
        "Test 2     : %.2f\n"
        "Assignment : %.2f\n"
        "Lab Test   : %.2f\n"
        "Final Exam : %.2f\n",
        pS->id, pS->last, pS->first,
        pS->test1, pS->test2, pS->assignment,
        pS->labtest, pS->finalexam
    );
}

void edit(pStudent pS)
{
    int ID = 0;
    int idTmp = pS->id; /* Firstly get a backup copy of this id ... */
    pS->id = -1; /* Now ... reset old id number to a dummy value ... */
    
    printf("Edit ID          : ");
    scanf("%d", &ID); flushStdin();
    if( pID(ID) ) /* i.e. if pointer returned is not NULL, this 'ID' IS USED */
    {
        printf("\nid %d is NOT available.\n", ID );
        pS->id = idTmp; /* Restore the id since this pS was NOT  edited */
        return; /* Exit to menu right now ... */
    }

    /* If reach hear ... */

    del(pS);/* Delete old record ... etc */

    pS = (pStudent) malloc(sizeof(Student)); /* get new memory for new record */
    myAssert( (pS!=NULL), "Error: malloc failed to allocte memory");
    pS->id = ID;

    printf("Edit Last Name   : ");
    pS->last = getLine(stdin);
    printf("Edit First Name  : ");
    pS->first = getLine(stdin);

    printf("Edit Test1       : ");
    scanf( "%f", &pS->test1 );      flushStdin();
    printf("Edit Test2       : ");
    scanf( "%f", &pS->test2 );      flushStdin();
    printf("Edit Assignment  : ");
    scanf( "%f", &pS->assignment ); flushStdin();
    printf("Edit Lab Test    : ");
    scanf( "%f", &pS->labtest );    flushStdin();
    printf("Edit Final Exam  : ");
    scanf( "%f", &pS->finalexam );  flushStdin();

    insert( pS );
}

void viewDel()
{
    pStudent p;
    int yes = 0, ID = 0;

    printf("Enter the id number to View/Delete : ");
    scanf("%d",&ID); flushStdin();

    p = pID(ID); /* See if pointer exists; i.e. get value or NULL */
    if( p ) /* i.e. if pointer returned above was not NULL ... */
    {
        view(p);
        printf("\nDelete (y/n) ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' ) { del(p); fileFlag = 1; }
        while( yes!='\n' ) yes=getchar(); /* flush stdin */
    }
    else printf("\nStudent with ID number %d not found.\n", ID);
}

void del(pStudent pS)
{
    pStudent p,
             pp; /* to hold the pointer to the previous record */

    /* Handle special case of 'first in list' */
    if( pS==phead )
    {
        phead = pS->next;
        free( pS->first );
        free( pS->last );
        
        free( pS );
        --numStudents; /* update globals ... */
        fileFlag = 1;
        return;
    }

    /* Else not first in list, so ... */

    p = phead; /* set p to this initial value to start loop */

    /* Now find the pointer to the previous record. */
    while( p != pS )
    {
        pp = p; /* pp holds previous pointer value ... */
        p = p->next; /* set to next pointer in link list chain */
    }

    /*
        Now we have a pointer to the previous Student record, so ...
        we can now point that previous record to one past this pS record
    */
    pp->next = pS->next;

    /* Now free the memory for this record and update the globals ... */
    free( pS->first );
    free( pS->last );
    
    free( pS);
    --numStudents;
    fileFlag = 1;
}

void delAll()
{
    pStudent p = phead; /* Set p to this initial value to start loop */
    pStudent pNext;     /* To hold the pointer to the next record */

    while( p != NULL )
    {
        pNext = p->next; /* Get a pointer to the next record. */
        free( p->first );
        free( p->last );
        
        free( p );
        p = pNext; /* Update p ... */
    }

    /* Update globals ...  */
    phead = NULL;
    numStudents = 0;
    fileFlag = 0;
}

/* Note: calling delAll() here ... will re-set globals as above ... */
void init()
{
    int yes;
    if( phead != NULL )
    {
        printf("\nDo you wish to overwrite the records in memory y/n ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' )
            delAll(); /* re-sets globals ... */
        else
        {
            if(numStudents==1)
                puts("1 Student record was left intact in memory.");
            else
                printf("%d Student records were left intact in memory.\n",
                       numStudents);
        }
        while( yes!='\n' ) yes=getchar(); /* flush stdin */
    }
    else puts("\nThere were no records in memory to clear.");
}

void scramble( char s[] )
{
    int i = 0;
    int code[] = {3,1,4,1,5,9,8,6,7,0,7,0,2,8,6,9,5,3,4,2};
    while( s[i]!=0 ) { s[i] = (char) ((int)s[i] - code[i]); ++i; }
}

void unscramble( char s[] )
{
    int i = 0;
    int code[] = {3,1,4,1,5,9,8,6,7,0,7,0,2,8,6,9,5,3,4,2};
    while( s[i]!=0 ) { s[i] = (char) ((int)s[i] + code[i]); ++i; }
}

void writeAllToFile()
{
    if( !fileFlag ) return;

    if( numStudents == writeFile() )
    {
        if( numStudents > 0 )
        {
            if( numStudents == 1 )
                puts("\nThe 1 Student record was safely filed.");
            else
                printf
                (
                    "\nAll %d Student records safely filed.\n",
                    numStudents
                );
        }
    }
    else
    {
        printf
        (
            "\nAn error occured while filing the Student records."
            "\nPlease see the programmer for help with the problem.\n"
            "\nExiting the program now.  Press 'Enter' to continue ... "
        );
        flushStdin(); /* Holds window open to show above message. */
        exit(0); /* Return error number to system. */
    }
}

int writeFile()
{
    int count; /* to track the records actually written to the file */
    FILE* fp;
    pStudent p = phead;

    if( phead==NULL )
    {
        puts("\nNo records available ... so NO records written to file.") ;
        return 0;
    }

    fp = fopen( fName, "w" );
    if( fp==NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", fName);
        flushStdin();
        return 0;
    }

    count = 0; /* to track the records actually written to the file */
    while( p!=NULL )
    {
        fprintf
        (
            fp,
            "%d\n"
            "%s\n"
            "%s\n"
            "%.2f\n"
            "%.2f\n"
            "%.2f\n"
            "%.2f\n"
            "%.2f\n",
            p->id, p->last, p->first,
            p->test1, p->test2, p->assignment,
            p->labtest, p->finalexam
        );

        ++count;
        p = p->next;
    }

    fclose( fp );
    fileFlag = 0;
    return count; /* Number of records written. */
}

int readFile()
{
    int count, ID;
    /* char buffer[maxStringLen+1]; */
    FILE *fp;

    pStudent pS;
#if !sortRead
    pStudent pp=NULL;
#endif

    fp = fopen( fName, "r" );
    if( fp==NULL )
    {
        printf
        (
            "\nError opening file %s.\n"
            "Perhaps it hasn't been created yet?\n"
            "Press 'Enter' to continue ... ",
            fName
        );
        flushStdin();
        return 0;
    }

    /* BUT!!! may need to delete any records in memory first. */
    init();

    /*
        NOTE: we need phead to be equal to NULL in the following ...
        to be permitted to set phead ...
        to point to the first Student record in memory (read in from the file).
    */
    if( phead != NULL ) /* then exit with message ... */
    {
        if(numStudents==1)
            puts("\nThe 1 former Student record was left in memory.");
        else
            printf("\nThe %d former Student records were left in memory.\n", numStudents);
        return 0; /* So ...old count of Student records will NOT be updated. */
    }

    /* If the program reaches here ... then ... */

    count = 0;

    while(  fscanf( fp, "%d", &ID) != EOF  )
    {
        pS = (pStudent) malloc(sizeof(Student));
        pS->id = ID;

        //fscanf( fp, "%s", buffer );
        //pS->last = newCopy( buffer );
        fgetc(fp); /* eats up the '\n' char left over from above 'ID read' */
        pS->last = getLine(fp);

        //fscanf( fp, "%s", buffer );
        //pS->first = newCopy( buffer );
        pS->first = getLine(fp);

        fscanf( fp, "%f", &pS->test1 );
        fscanf( fp, "%f", &pS->test2 );
        fscanf( fp, "%f", &pS->assignment );
        fscanf( fp, "%f", &pS->labtest );
        fscanf( fp, "%f", &pS->finalexam );
#if !sortRead
        if ( pp != NULL )   /* for 2nd and up records ...*/
            pp->next = pS;  /* now previous record points to this new one */
        else phead =pS;     /* first record only has base pointer */

        pS->next = 0; /* NULL terminate list ... */
        pp = pS; /* Keep a copy of address of this pS in pp for next while loop. */
#else
        insert( pS );
#endif
        ++count;
        /* printf( "\nRecord number is %d\n", count ); */
    }
    fclose( fp );
    if(count==1)
        puts("\n1 record was read into memory from the disk file.");
    else
        printf("\n%d records were read into memory from the disk file.\n", count);

    fileFlag = 0;
    return count; /* Total number of Student records found in the file. */
}

int passWord()
{
    /*
        Get the password in the file, if it exists, into buffer2
        and compare it with the user entry in buffer1.
    */
    char *buffer1;
    char buffer2[maxStringLen+1];
    int attempts;
    int pwOK = 0;
    
    FILE *fp = fopen(fPW, "r");
    if(fp==NULL) /* i.e. if file fPW doesn't exit ... */
    {
        pwOK = newPW(); /* get new password ...*/
        if( pwOK == 1 ) return 1; /* report this match of passwords back ...*/
    }
    else /* File fPW does exist ... so ... */
    {
        /* Get file password into a string in buffer2. */
        fscanf( fp, "%s", buffer2 );
        fclose( fp );
        unscramble( buffer2 );

        /* Now ... get password entered by user into a string in buffer1. */
        for( attempts=0; attempts<3; ++attempts )
        {
            printf("Enter password: ");
            //scanf( "%s", buffer1 ); flushStdin();
            buffer1 = getLine(stdin);
            if(strcmp(buffer1, buffer2)==0) /* If true ... passwords matched. */
            {
                puts( "Match!\n" );
                free( buffer1 );
                return 1; /* Report this match of passwords back ...*/
            }
            free( buffer1 );
        }
    }
    /* if reach here ...*/

    printf( "NO MATCH! ... Press 'Enter' to exit ... " );
    flushStdin();
    exit(1); /* Quit the program right now ... */
}

int newPW()
{
    FILE* fp;
    char* buffer1;
    char* buffer2;

    for(;;)
    {
         /* Get the new password into a string in buffer1. */
        printf("Enter the new password (8 to 20 characters): ");
        buffer1 = getLine(stdin); /* a max of maxStringLen char's */
        if( strlen(buffer1) >= minPWLen )
            break;

        printf("Your password must be at least %d characters ...\n", minPWLen );
        free( buffer1 ); /* ... and try again ...*/
    }

    /*
        Get a 'verify copy' of the new password into buffer2
        and compare it with the password in buffer1.
    */
    printf("Enter the new password again: ");
    buffer2 = getLine(stdin);
    if(strcmp(buffer1, buffer2)==0) /* If true ... passwords matched. */
    {
        fp = fopen(fPW, "w");
        if(fp==NULL)
        {
            printf("Error opening file %s ... Press 'Enter' to exit ... ", fPW);
            flushStdin();
            free(buffer2);
            free(buffer1);
            exit(2);
        }
        else
        {
            puts( "Match!\n" );
            scramble(buffer1);
            fprintf( fp, "%s", buffer1 );
            fclose( fp );
            free(buffer2);
            free(buffer1);
            return 1; /* Report this match of passwords back ...*/
        }
    }

    /* If reach here ...*/

    printf( "NO MATCH! ... Press 'Enter' to exit ... " );
    flushStdin();
    free(buffer2);
    free(buffer1);
    exit(3); /* Quit the program right now ... */
}

/* A function to compare two Student records to permit sorting ... */
int StudentCmp( pStudent pS1, pStudent pS2 )
{
    int compare = strcmp(pS1->last, pS2->last);
    if ( compare == 0 )
        return strcmp(pS1->first, pS2->first);

    return compare;
}
Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2009, 12:19:39 PM
And another C program using a linked list of potential student interest ...

Code: [Select]
/*
    Student Registration program in C using a linked-list of struct Student

    9 data items ...

    1. Student national id ... an unique integer > 0
    2. and 3. Student names last, first ... C strings
    4. Date of birth dob (can calculate age from this) yyyymmdd
    5. CXC (subjects) grades: I, II, III, ..., VI
                          and A, B, C ..., F (profile grades)
    6. CAPE(subjects) grades: I, II, III, ..., VII
                          and A, B, C, ..., G (profile grades)
    Core Subjects 1..6 ... the grades can be input like this: 112233AABBCC
    to signify for #'ed subjects below 1=1, 2=1, 3=2, 4=2, 5=3, 6=3
                                       1=A, 2=A, 3=B, 4=B, 5=C, 6=6, etc ...
        1 - Caribbean Studies,
        2 - Communication Studies,
        3 - Functional French,
        4 - Functional Spanish,
        5 - Information Technology, and
        6 - Statistical Analysis

    7. Current area of study ... a C string
    8. Desired area of study ... a C string
    9. Other: any other Necessary Personal information ... a C string
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* re. strlen, strcmp */

/* Globals ... */

#define HEADER1 "Student CXC and CAPE Record Management System ... "
#define HEADER2 "1. (I)nitialize Student record list in RAM\n" \
                "2. (A)dd i.e. insert in order, by name, a NEW Student record\n" \
                "3. view/(D)elete a Student record\n" \
                "4. view/(E)dit a Student record\n" \
                "5. (S)how all Student records (as inserted BY NAME above)\n" \
                "6. (F)ile all Student records presently in RAM\n" \
                "7. (R)ead all Student records on file into RAM\n" \
                "8. set/change (P)assword\n" \
                "9. e(X)it"

#define FILE_NAME "StudentCXCCAPE.txt"
#define FILE_PW "StudentPW.txt"
#define MIN_PW_LEN 8

#define READ_SORTED 1

typedef struct Student
{
    long id; /* 1. Student national id ... an unique integer > 0 */
    char* last;  /* any length C string */
    char* first; /* any length ... */
    long dob; /* enter/store as: yyyymmdd */
    char sCX[13]; /* 12+1 ... enter/store like this: 112233AABBCC   */
    char sCA[15]; /* 14+1 ... enter/store like this: 1112233AAABBCC */
    char* currentArea; /* any length ... */
    char* desiredArea; /* any length ... */
    char* otherInfo;   /* any length ... */
    struct Student* next;
}Student;

typedef Student* pStudent;

/* using these global var's to keep function calls simple ... */
pStudent pHead = NULL;
int numStudents = 0; /* to hold 'the number of Student records in memory' ...*/
int pwOK = 0;        /* to hold 'the password was OK' flag; 0=false & 1=true */
int fileFlag = 0;    /* fileFlag indicates 'the need to update the file' ... */

/* forward declarations */

pStudent pID(int n);
char* getLine( FILE* fp );
void add();
void insert( pStudent pS );
void flushStdin();
void showAll();
void viewEdit();
void viewDel();
void view(pStudent pS);
void edit(pStudent pS);
void del(pStudent pS);
void delAll();
void init();
void scramble( char s[] );
void unscramble( char s[] );
void writeAllToFile();
void exitNowYes();
int writeFile();
int readFile();
int passWord();
int newPW();
int StudentCmp( pStudent pS1, pStudent pS2 );

int menu()
{
    int c, selection;
    puts( HEADER1 );
    if( numStudents == 1 ) puts( "Presently there is 1 record in RAM.\n" );
    else printf( "Presently there are %d records.\n\n", numStudents );
    puts( HEADER2 );
    printf( "\nPlease enter your selection: " );
    selection = c = getchar();
    while( c!='\n' ) c=getchar(); /* 'flush' stdin */
    return selection;
}

void myAssert( int condition, char text[] )
{
    if( !condition )
    {
        fprintf( stderr, "%s.  Press 'Enter' to exit ... ", text );
        getchar();
        exit(1);
    }
}


int main()/* * * * * * * * * * * * * begin main * * * * * * * * * * * * * * * */
{
    pwOK = passWord();
    numStudents = readFile();
    for(;;) /* Loop forever ... until 'return' ... */
    {
        int choice;
        if( !pwOK )
            pwOK = passWord();

        choice = -1; /*  error flag */
        choice = menu();
        /* printf("You picked %d ... \n", choice ); */
        switch( choice )
        {
            case '1': case 'i': case 'I': init(); break; /* out of 'switch' */
            case '2': case 'a': case 'A': add();  break;
            case '3': case 'd': case 'D': viewDel(); break;
            case '4': case 'e': case 'E': viewEdit(); break;
            case '5': case 's': case 'S': showAll(); break;
            case '6': case 'f': case 'F': writeAllToFile(); break;
            case '7': case 'r': case 'R':
            {
                /* If any were read ... will update Global variable pHead */
                int readNum = readFile();
                if( readNum > 0 )
                    numStudents = readNum; /* update Global numStudents */
            }
            break;
            
            case '8': case 'p': case 'P': pwOK = newPW(); break;
            case '9': case 'x': case 'X': exitNowYes(); break;
            default: puts("\nNot implemented yet ...");
        } /* end switch */
    } /* end for ... */

    return 0;

} /* * * * * * * * * * * * * * * * * * end main * * * * * * * * * * * * * * * */

The function definitions are given on the next page ...
Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2009, 12:22:14 PM
Function definitions are given here for the C linked-list student program above ...

Code: [Select]
/* Function definitions ... */

char* getLine( FILE* fp )
{
    int buf_chunk = 256;
    int buf_len = buf_chunk;
    char* buffer = (char*) calloc( buf_len, sizeof(char) );
    int i=0, c;
    myAssert( (buffer!=NULL), "Error: calloc failed to allocate memory" );
    /* eats up WHOLE line ... including '\n' */
    while( (c=fgetc(fp)) != EOF && c != '\n' )
    {
        if( i == buf_len-2 )
        {
            buf_len += buf_chunk;
            buffer = (char*) realloc( buffer, buf_len );
            myAssert( (buffer!=NULL), "Error: realloc failed to allocate memory" );
        }
        buffer[i++] = c; /* first assign then increment i ... */
    }

    buffer[i] = 0; /* confirm NULL terminated ... */
    buffer = (char*) realloc( buffer, i+1  );
    myAssert( (buffer!=NULL), "Error: realloc failed to allocate memory" );
    return buffer;
}


/* Return a pointer to the Student if ID found in list; else return NULL */
pStudent pID( int n )
{
    pStudent p = pHead;
    if( p==NULL )
        return NULL; /* List empty ... so all id numbers are available */

    while( p!=NULL )
    {
        if( p->id == n )
            return p; /* id number entered was already used */

        /* Else ... get next p to compare */
        p = p->next;
    }

    /* If reach here ... then id number not in list so ... */
    return NULL;
}


void flushStdin()
{
    while( getchar() != '\n' );
}

void exitNowYes()
{
    int c, quit;

    printf("\nPress 'X' to eXit  ... ");
    c = quit = getchar();
    while(c != '\n') c=getchar(); /* flush stdin ... */
    if( quit=='x' || quit=='X' )
    {
        if( fileFlag )
            writeAllToFile();

        printf("\nPress 'Enter' to exit right now ... or 'c' to continue ... ");
        if( (c=getchar()) == '\n' )
        {
            FILE* fp = fopen( FILE_NAME, "r" );
            if( fp==NULL )
            {
                printf
                (
                    "\nError opening file %s.  Perhaps it doesn't exit yet."
                    "\nPress 'Enter' to exit ... ",
                    FILE_NAME
                );
                flushStdin();
                exit(0);
            }

            /* if reach here ... can show the file using a system editor ... */
            fclose( fp );

            /* if using a Win OS ... */
            system( "notepad " FILE_NAME );
            exit(0);
        }
        else
        {
            puts("\nOk ... Exit aborted ...");
            while(c != '\n') c=getchar(); /* flush stdin ... */
        }
    }
}

void add() /* ... and insert in the proper place in the list. */
{
    pStudent pS;
    long ID = 0;

    printf("Enter ID                     : ");
    scanf("%ld", &ID); flushStdin();

    if( pID(ID) || ID<=0  ) /* i.e. if pointer returned is NOT NULL, the id IS used */
    {
        printf("\nid %ld is NOT available.\n", ID );
        return; /* Exit to menu right now ... */
    }
    /* If program reaches here, the ID just entered is available to use. */
    pS = (pStudent) malloc(sizeof(Student));
    myAssert( (pS!=NULL), "Error: malloc failed to allocate memory" );
    pS->id = ID;

    printf("Enter Last Name              : ");
    pS->last = getLine(stdin);
    printf("Enter First Name             : ");
    pS->first = getLine(stdin);

    printf("Enter DOB yyyymmdd           : ");
    scanf( "%ld", &pS->dob );       flushStdin();
    printf("Enter CXC as 123456ABCDEF    : ");
    scanf( "%12s", pS->sCX );      flushStdin();
    printf("Enter CAPE as 1234567ABCDEFG : ");
    scanf( "%14s", pS->sCA );      flushStdin();
    printf("Enter current area           : ");
    pS->currentArea = getLine(stdin);
    printf("Enter desired area           : ");
    pS->desiredArea = getLine(stdin);
    printf("Enter other info             : ");
    pS->otherInfo = getLine(stdin);

    /* Set up link to this node */
    insert( pS );
}

/* insert in list with last & first names in proper order */
void insert( pStudent pS )
{
    pStudent q;

    /* Firstly, we handle the case where 'this' should be the first element. */
    if(pHead == NULL || StudentCmp(pS, pHead) < 0)
    {
        /* So ... it now becomes the first element ... */
        pS->next = pHead; /* old pHead becomes 2nd in list ... */
        pHead = pS; /* and ... this pS ... becomes the head of the list */
    }
    else /* If here ... search the linked list for the right location ... */
    {
        q = pHead; /* Get a working copy of pHead in q */
        /* Start comparing with element AFTER 'q' ... i.e. the 'next in' ... */
        while(q->next != NULL && StudentCmp(q->next, pS) <= 0)
        {
            q = q->next; /* Traverse the list ... */
        }
        /*
            Ok, insert after 'q' by relinking the pointers (similar to above)
            ( Includes inserting at end position ... where q->next == NULL )
        */
        pS->next = q->next; /* Inserted 'pS' is linked to 'upstream element' */
        q->next = pS;  /* Now 'q' is updated to link to the new 'pS' element */
    }

    /* Update global variables. */
    ++numStudents;
    fileFlag = 1;
}

void showAll()
{
    pStudent p = pHead;
    int c;

    if(pHead==NULL)
    {
        puts("\nNo records in memory at present.") ;
        return;
    }

    /* If reach here ... */

    while( p!=NULL )
    {
        view( p ); /* show this record ...*/

        printf("\nPress 'Enter' to continue ... or enter 'A' to abort ... ");
        if( (c=getchar()) == 'a' || c == 'A' )
        {
            flushStdin();
            return;
        }
        while(c != '\n') c=getchar(); /* flush stdin ... */
        p = p->next;
    }
}

void viewEdit()
{
    int yes = 0;
    long ID = 0;
    pStudent p;

    printf("Enter the id number to View/Edit : ");
    scanf("%ld",&ID); flushStdin();

    p = pID(ID); /* See if pointer exists; i.e. get value or NULL */
    if( p )     /* i.e. if pointer returned above was not NULL ... */
    {
        view(p);
        printf("\nEdit (y/n) ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' )
        {
            while( yes!='\n' ) yes=getchar(); /* flush stdin */
            edit(p);
        }
        else while( yes!='\n' ) yes=getchar(); /* flush stdin */
    }
    else printf("\nStudent with ID number %ld not found.\n", ID);
}

void view(pStudent pS)
{
    printf
    (
        "ID number    : %ld\n"
        "Last name    : %s\n"
        "First name   : %s\n"
        "DOB          : %ld\n"
        "CXC          : %s\n"
        "CAPE         : %s\n"
        "Current area : %s\n"
        "Desired area : %s\n"
        "Other info   : %s\n",
        pS->id, pS->last, pS->first,
        pS->dob, pS->sCX, pS->sCA,
        pS->currentArea, pS->desiredArea, pS->otherInfo
    );
}

void edit(pStudent pS)
{
    long ID = 0;
    long idTmp = pS->id; /* Firstly get a backup copy of this id ... */

    pS->id = -1; /* Now ... reset old id number to a dummy value ... */

    printf("Edit ID           : ");
    scanf("%ld", &ID); flushStdin();

    if( pID(ID) ) /* i.e. if pointer returned is not NULL, this 'ID' IS USED */
    {
        printf("\nid %ld is NOT available.\n", ID );
        pS->id = idTmp; /* Restore the id since this pS was NOT  edited */
        return; /* Exit to menu right now ... */
    }

    /* If reach hear ... */

    del(pS);/* Delete old record ... etc */

    pS = (pStudent) malloc(sizeof(Student)); /* get new memory for new record */
    myAssert( (pS!=NULL), "Error: malloc failed to allocate memory" );
    pS->id = ID;

    printf("Edit Last Name    : ");
    pS->last = getLine(stdin);
    printf("Edit First Name   : ");
    pS->first = getLine(stdin);

    printf("Edit DOB          : ");
    scanf( "%ld", &pS->dob );        flushStdin();
    printf("Edit CXC          : ");
    scanf( "%12s", pS->sCX );       flushStdin();
    printf("Edit CAPE         : ");
    scanf( "%14s", pS->sCA );       flushStdin();
    printf("Edit current area : ");
    pS->currentArea = getLine(stdin);
    printf("Edit desired area : ");
    pS->desiredArea = getLine(stdin);
    printf("Edit other info   : ");
    pS->otherInfo = getLine(stdin);

    insert( pS );
}

void viewDel()
{
    pStudent p;
    int yes = 0;
    long ID = 0;

    printf("Enter the id number to View/Delete : ");
    scanf("%ld",&ID); flushStdin();

    p = pID(ID); /* See if pointer exists; i.e. get value or NULL */
    if( p ) /* i.e. if pointer returned above was not NULL ... */
    {
        view(p); /* show this record ... */

        printf("\nDelete (y/n) ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' ) del(p);
        while( yes!='\n' ) yes=getchar(); /* flush stdin */
    }
    else printf("\nStudent with ID number %ld not found.\n", ID);
}

void del(pStudent pS)
{
    pStudent p,
             pp; /* to hold the pointer to the previous record */

    /* Handle special case of 'first in list' */
    if( pS==pHead )
    {
        pHead = pS->next;

        free( pS->first );
        free( pS->last );
        free( pS->currentArea );
        free( pS->desiredArea );
        free( pS->otherInfo );

        free( pS );

        --numStudents; /* update globals ... */
        fileFlag = 1;
        return;
    }

    /* Else not first in list, so ... */

    p = pHead; /* set p to this initial value to start loop */

    /* Now find the pointer to the previous record. */
    while( p != pS )
    {
        pp = p; /* pp holds previous pointer value ... */
        p = p->next; /* set to next pointer in link list chain */
    }

    /*
        Now we have a pointer to the previous Student record, so ...
        we can now point that previous record to one past this pS record
    */
    pp->next = pS->next;

    /* Now free the memory for this record and update the globals ... */
    free( pS->first );
    free( pS->last );
    free( pS->currentArea );
    free( pS->desiredArea );
    free( pS->otherInfo );

    free( pS);

    --numStudents;
    fileFlag = 1;
}

void delAll()
{
    pStudent p = pHead; /* Set p to this initial value to start loop */
    pStudent pNext;     /* To hold the pointer to the next record */

    while( p != NULL )
    {
        pNext = p->next; /* Get a pointer to the next record. */

        free( p->first );
        free( p->last );
        free( p->currentArea );
        free( p->desiredArea );
        free( p->otherInfo );

        free( p );

        p = pNext; /* Update p ... */
    }

    /* Update globals ...  */
    pHead = NULL;
    numStudents = 0;
    fileFlag = 0;
}

/* Note: calling delAll() here ... will re-set globals as above ... */
void init()
{
    int yes;
    if( pHead != NULL )
    {
        printf("\nDo you wish to overwrite the records in memory y/n ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' )
            delAll(); /* re-sets globals ... */
        else
        {
            if(numStudents==1)
                puts("1 Student record was left intact in memory.");
            else
                printf("%d Student records were left intact in memory.\n",
                       numStudents);
        }
        while( yes!='\n' ) yes=getchar(); /* flush stdin */
    }
    else puts("\nThere were no records in memory to clear.");
}

void scramble( char s[] )
{
    int i = 0;
    int code[] = {3,1,4,1,5,9,8,6,7,0,7,0,2,8,6,9,5,3,4,2};
    while( s[i]!=0 ) { s[i] = (char) ((int)s[i] - code[i]); ++i; }
}

void unscramble( char s[] )
{
    int i = 0;
    int code[] = {3,1,4,1,5,9,8,6,7,0,7,0,2,8,6,9,5,3,4,2};
    while( s[i]!=0 ) { s[i] = (char) ((int)s[i] + code[i]); ++i; }
}

void writeAllToFile()
{
    if( !fileFlag )
    {
        puts("\nNo changes ... so nothing filed ...");
        return;
    }

    if( numStudents == writeFile() ) /* write all to file ... */
    {
        if( numStudents > 0 )
        {
            if( numStudents == 1 )
                puts("\nThe 1 Student record was safely filed.");
            else
                printf
                (
                    "\nAll %d Student records safely filed.\n",
                    numStudents
                );
        }
    }
    else
    {
        printf
        (
            "\nAn error occured while filing the Student records."
            "\nPlease see the programmer for help with the problem.\n"
            "\nExiting the program now.  Press 'Enter' to continue ... "
        );
        flushStdin(); /* Holds window open to show above message. */
        exit(0); /* Return error number to system. */
    }
}

int writeFile()
{
    int count; /* to track the records actually written to the file */
    FILE* fp;
    pStudent p = pHead;

    if( pHead==NULL )
    {
        puts("\nNo records available ... so NO records written to file.") ;
        return 0;
    }

    fp = fopen( FILE_NAME, "w" );
    if( fp==NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", FILE_NAME);
        flushStdin();
        return 0;
    }

    count = 0; /* to track the records actually written to the file */
    while( p!=NULL )
    {
        fprintf
        (
            fp,
            "%ld\n"
            "%s\n"
            "%s\n"
            "%ld\n"
            "%s\n"
            "%s\n"
            "%s\n"
            "%s\n"
            "%s\n",
            p->id, p->last, p->first,
            p->dob, p->sCX, p->sCA,
            p->currentArea, p->desiredArea, p->otherInfo
        );

        ++count;
        p = p->next;
    }

    fclose( fp );
    fileFlag = 0;
    return count; /* Number of records written. */
}

int readFile()
{
    int count;
    long ID;
    FILE *fp;

    pStudent pS;
#if !READ_SORTED
    pStudent pp=NULL;
#endif

    fp = fopen( FILE_NAME, "r" );
    if( fp==NULL )
    {
        printf
        (
            "\nError opening file %s.\n"
            "Perhaps it hasn't been created yet?\n"
            "Press 'Enter' to continue ... ",
            FILE_NAME
        );
        flushStdin();
        return 0;
    }

    /* BUT!!! may need to delete any records in memory first. */
    init();

    /*
        NOTE: we need pHead to be equal to NULL in the following ...
        to be permitted to set pHead ...
        to point to the first Student record in memory (read in from the file).
    */
    if( pHead != NULL ) /* then exit with message ... */
    {
        if(numStudents==1)
            puts("\nThe 1 former Student record was left in memory.");
        else
            printf("\nThe %d former Student records were left in memory.\n", numStudents);
        return 0; /* So ...old count of Student records will NOT be updated. */
    }

    /* If the program reaches here ... then ... */

    count = 0;

    while(  fscanf( fp, "%ld", &ID) != EOF  )
    {
        pS = (pStudent) malloc(sizeof(Student));
        myAssert( (pS!=NULL), "Error: malloc failed to allocate memory" );
        pS->id = ID;
        fgetc(fp); /* eats up the '\n' char left over from above 'ID read' */

        pS->last = getLine(fp);
        pS->first = getLine(fp);

        fscanf( fp, "%ld", &pS->dob );
        fgetc(fp); /* eats up the '\n' char left over ... */

        fscanf( fp, "%s", pS->sCX );
        fscanf( fp, "%s", pS->sCA );
        fgetc(fp); /* eats up the '\n' char left over ...  */

        pS->currentArea = getLine(fp);
        pS->desiredArea = getLine(fp);
        pS->otherInfo   = getLine(fp);
#if !READ_SORTED
        if ( pp != NULL )   /* for 2nd and up records ...*/
            pp->next = pS;  /* now previous record points to this new one */
        else pHead =pS;     /* first record only has base pointer */

        pS->next = 0; /* NULL terminate list ... */
        pp = pS; /* Keep a copy of address of this pS in pp for next while loop. */
#else
        insert( pS );
#endif
        ++count;
        /* printf( "\nRecord number is %d\n", count ); */
    }
    fclose( fp );
    if(count==1)
        puts("\n1 record was read into memory from the disk file.");
    else
        printf("\n%d records were read into memory from the disk file.\n", count);

    fileFlag = 0;
    return count; /* Total number of Student records found in the file. */
}
Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2009, 12:23:50 PM
The last few function definitions from the linked-list student program above are given here ...

Code: [Select]
int passWord()
{
    /*
        Get the password in the file, if it exists, into buffer2
        and compare it with the user entry in buffer1.
    */
    char* buffer1;
    char* buffer2;
    int attempts;

    int pwOK = 0;
    FILE* fp;
    fp = fopen(FILE_PW, "r");

    if(fp==NULL) /* i.e. if file FILE_PW doesn't exit ... */
    {
        pwOK = newPW(); /* get new password ...*/
        if( pwOK == 1 ) return 1; /* report this match of passwords back ...*/
    }
    else /* File FILE_PW does exist ... so ... */
    {
        /* Get file password into a string in buffer2 */
        buffer2 = getLine( fp );
        fclose( fp );
        unscramble( buffer2 );

        /* Now ... get password entered by user into a string in buffer1 */
        for( attempts=0; attempts<3; ++attempts )
        {
            printf("Enter password: ");
            buffer1 = getLine(stdin);
            if(strcmp(buffer1, buffer2)==0) /* If true ... passwords matched */
            {
                puts( "Match!\n" );
                free( buffer1 );
                free( buffer2 );
                return 1; /* Report this match of passwords back ...*/
            }
            free( buffer1 );
        }
    }
    /* if reach here ...*/
    free( buffer2 );
    printf( "NO MATCH! ... Press 'Enter' to exit ... " );
    flushStdin();
    exit(1); /* Quit the program right now ... */
}

int newPW()
{
    FILE* fp;
    char* buffer1;
    char* buffer2;

    for(;;)
    {
         /* Get the new password into a string in buffer1 */
        printf
        (
            "Enter the new password with at least %d characters: ",
            MIN_PW_LEN
        );
        buffer1 = getLine(stdin);
        if( strlen(buffer1) >= MIN_PW_LEN )
            break;

        printf("Your password must be at least %d characters ...\n", MIN_PW_LEN );
        free( buffer1 ); /* ... and try again ...*/
    }

    /*
        Get a 'verify copy' of the new password into buffer2
        and compare it with the password in buffer1.
    */
    printf("Enter the new password again: ");
    buffer2 = getLine(stdin);
    if(strcmp(buffer1, buffer2)==0) /* If true ... passwords matched */
    {
        fp = fopen(FILE_PW, "w");
        if(fp==NULL)
        {
            printf("Error opening file %s ... Press 'Enter' to exit ... ", FILE_PW);
            flushStdin();
            free(buffer2);
            free(buffer1);
            exit(2);
        }
        else
        {
            puts( "Match!\n" );
            scramble(buffer1);
            fprintf( fp, "%s", buffer1 );
            fclose( fp );
            free(buffer2);
            free(buffer1);
            return 1; /* Report this match of passwords back ...*/
        }
    }

    /* If reach here ...*/

    printf( "NO MATCH! ... Press 'Enter' to exit ... " );
    flushStdin();
    free(buffer2);
    free(buffer1);
    exit(3); /* Quit the program right now ... */
}

/* A function to compare two Student records to permit sorting ... */
int StudentCmp( pStudent pS1, pStudent pS2 )
{
    int compare = strcmp(pS1->last, pS2->last);
    if ( compare == 0 )
        return strcmp(pS1->first, pS2->first);

    return compare;
}


Here is a demo of qsort in C ... int's, C string's, struct's ...
(and also demo's sorts that ignore case)

Code: [Select]
/* C qsort examples ... NOTE: needs a C compiler with qsort and const var's */

/* the version 2010-05-16 */

/*
    demos ... qsort ...
    sorting an array of struct on various fields ... and combined fields
    sorting an array of C strings with (and without) ignoring case
    sorting an array of int's
*/

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

typedef struct Contacts
{
    char name[16];
    int  area;
    int  phone;
} Contacts;

int compareInt( const void* x, const void* y )
{
    return( *(int*)x - *(int*)y );
}

int compareStr( const void* x, const void* y )
{
    return strcmp( *(char**)x, *(char**)y );
}

int compareStrIgnoreCase( const void* x, const void* y )
{
    char* s1 = *(char**)x;
    char* s2 = *(char**)y;
    while( *s1 != 0 && *s2 != 0 )
    {
        if( tolower(*s1) != tolower(*s2) )
            break;
        ++s1; ++s2;
    }
    
    /* now test end conditions .... */
    return tolower(*s1) - tolower(*s2);
}

int compareContactsNameIgnoreCase( const void* x, const void* y )
{
    Contacts* c1 = (Contacts*)x;
    Contacts* c2 = (Contacts*)y;
    int i = 0;
    while( c1->name[i] != 0 && c2->name[i] != 0 )
    {
        if( tolower(c1->name[i]) != tolower(c2->name[i]) )
            break;
        ++i;
    }
    /* now test end conditions .... */
    if( c1->name[i] == 0  &&   c2->name[i] == 0 )
    {
        if( c1->area == c2->area ) return c1->phone - c2->phone;
        else return c1->area - c2->area;
    }
    /* else ... */
    return tolower(c1->name[i]) - tolower(c2->name[i]);
}

int compareContactsPhone( const void* x, const void* y )
{
    Contacts* c1 = (Contacts*)x;
    Contacts* c2 = (Contacts*)y;
    if( c1->area == c2->area && c1->phone == c2->phone )
        return strcmp(c1->name, c2->name);
    if( c1->area == c2->area )
        return c1->phone - c2->phone;
    return c1->area - c2->area;
}

void print_book( Contacts c[], int size );



int main () /* * * * * * * * * * * MAIN BEGINS * * * * * * * * * * * * * * * */
{
    /* get some test data into arrays of type struct, C string, int ... */
    Contacts book[] =
    {
        {"joe", 416, 4442388}, {"zam", 519, 1234567}, {"anne", 705, 1234455},
        {"Tam", 905, 5555555}, {"Fran", 416, 7778900}, {"Anne", 705, 1116789 }
    };

    char* aryStr[] = { "joe", "zam", "anne", "Tam", "Fran", "Anne"  };

    int values[] = { 40, 10, 100, -1, 123, -7, 0, 90, 20, 25 };
    
    /* qsort an array of type int ... */
    int i,
        len = sizeof values / sizeof values[0];
    qsort( values, len, sizeof(int), compareInt );
    for( i = 0; i < len; ++i )
        printf( "%d ", values[i] );
    puts( "" );
    
    /* qsort an array of type C string, i.e. an array of pointers to char ... */
    len = sizeof aryStr / sizeof aryStr[0];
    qsort( aryStr, len, sizeof(char*), compareStr );
    for( i = 0; i < len; ++i )
        printf( "%s ", aryStr[i] );
    puts( "" );

    /* or sorted with case ignored ... */
    qsort( aryStr, len, sizeof(char*), compareStrIgnoreCase );
    for( i = 0; i < len; ++i )
        printf( "%s ", aryStr[i] );
    puts( "" );
    
    /* show unsorted array of struct ... */
    len = (sizeof book / sizeof book[0]);
    print_book( book, len );
    puts( "" );
    
    /* sort array of struct under name field FIRST ... and ignore case ... */
    qsort( book, len, sizeof(Contacts), compareContactsNameIgnoreCase );
    print_book( book, len );
    puts( "" );
    
    /* sort array of struct under phone fieldS FIRST ...  */
    qsort( book, len, sizeof(Contacts), compareContactsPhone );
    print_book( book, len );

    printf( "\nPress 'Enter' to continue ... " );
    getchar();
    return 0;

}/* * * * * * * * * * * * * * * * * MAIN ENDS * * * * * * * * * * * * * * * * */



void print_book( Contacts c[], int size )
{
    int i;
    for( i = 0; i < size; ++i )
        printf( "%-15s %3d-%7d\n", c[i].name, c[i].area, c[i].phone );
}
Title: Re: New thread especially for students of C and C++
Post by: David on November 23, 2009, 06:01:06 PM
Updates:

You also might like to see this new link at ...

http://sites.google.com/site/andeveryeyeshallseehim/

Shalom.

David
Title: Re: New thread especially for students of C and C++
Post by: David on April 11, 2010, 09:29:55 AM
Cvec: Here is the beginning of an emulation, in C, of the C++ push_back of a vector ...

(And following this Cvec ... is an emulation, Clist, that uses a stable mergesort for that linked-list of struct's ... instead of the qsort used for this 'Cvec' dynamic array of struct's.)

(Note: file readLine.h ... and file readWord.h ... are provided below this Cvec example.)

Code: [Select]
/* Rec's4qsortEditDelete2.c */
moved to here ...

http://developers-heaven.net/forum/index.php/topic,466.msg671.html#msg671

(See also the other pages there for CVec and Clist upgrades and example programs.)


Title: Re: New thread especially for students of C and C++
Post by: David on April 11, 2010, 09:30:46 AM
Ok ... here is the file readLine.h that is needed for the above program:

Update: latest versions of readLine.h and readWord.h are now maintained  here ...

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

The following the test program demo's using readLine and readWord  ....

Code: [Select]
/* readLine_readWord_testProgram.c */  /* this ver 2016-10-09 */


/* needs file readWord.txt in same folder */


#include "readWord.h" /* includes stdio.h, stdlib.h, string.h and myAssert */
#include "readLine.h"


#define TEST_FILE "readWord.txt" /* file used also to test readLine ... */
#define ARY_SIZE 5



int main()
{
    int i, j;
    char last;
    char* word, * line;
    char* raggedAry[ARY_SIZE];
    char delimits[] = "\n\t ,.;:";
    FILE* fp = fopen( TEST_FILE, "r" );
    myAssert( (fp != NULL), "Error: file '" TEST_FILE "' failed to open." );

    /* 1. testing readLine ... */

    puts( " ===> LINES FROM FILE '" TEST_FILE "' ===>" );
    j = 0;
    while( (line = readLine(fp)) )
    {
        printf( "%03d:%s\n", ++j, line );
        free( line );
    }
    puts( "===" );
    /* fclose( fp ); */

    printf( "\n ===> LINES FROM KEY BOARD ===>\n"
            "<Enter up to %d lines>\n", ARY_SIZE );
    i = 0;
    while
    (
        i < ARY_SIZE &&
        printf( "<Press 'Enter' to exit> Enter line %d: ", i+1 ) &&
        (line = readLine(stdin))
    )
    {
        if( line[0] == 0 ) { free( line ); break; }
        raggedAry[i++] = line;
    }

    puts( "\nYou entered ..." );
    for( j = 0; j < i; ++j )
    {
        printf( "<%03d> '%s'\n", j+1, raggedAry[j] );
        free( raggedAry[j] );
    }

    fputs( " ===> Press 'Enter' to continue ===> ", stdout );
    getchar();
   
    /* 2. testing readWord ... */
/*
    fp = fopen( TEST_FILE, "r" );
    myAssert( (fp != NULL), "Error: File '" TEST_FILE "' failed to open." );
*/
    rewind( fp ); /* can just rewind, since file left unclosed above ... */
   
    puts( "\n ===> WORDS FROM '" TEST_FILE "' ===>" );
    while( (word = readWord(fp, 8, delimits, &last)) )
    {
        printf( "%s\n", word );
        free( word );
    }
    puts( "===" );

    fclose( fp );

    i = 0;
    printf( "\n ===> VIA KEY BOARD enter up to %d words ===>", ARY_SIZE );
    printf( "\nEnter words (Enter q to quit)  : " );
    while
    (
        i < ARY_SIZE &&
        (word = readWord(stdin, 4, delimits, &last))
    )
    {
        if( ((word[0] == 'q' || word[0] == 'Q') && word[1] == 0) )
        {
            free( word );
            break;
        }
        raggedAry[i++] = word;
    }

    puts( "\nYou entered ..." );
    for( j = 0; j < i; ++j )
    {
        printf( "%2d: %s\n", j+1, raggedAry[j] );
        free( raggedAry[j] );
    }

    fputs( " ===> Press 'Enter' to exit program ===> ", stdout );
    fflush( stdout );
   
    /* flush stdin buffer ... */
    while( last != EOF && last != '\n' ) last = getchar();
    getchar();
    return 0;
}

/*  file name: "readWord.txt" */
/*
abd def
ghi,jkl

mn,    opqrst     uv;wxyz,
mn,    opqrst    ;   ;    uv,

wxyz
,


,
*/

/* output ... */
/*
 ===> LINES FROM FILE 'readWord.txt' ===>
001:abd def
002:ghi,jkl
003:
004:mn,    opqrst     uv;wxyz,
005:mn,    opqrst    ;   ;    uv,
006:
007:wxyz
008:,
009:
010:
011:,
===

 ===> LINES FROM KEY BOARD ===>
<Enter up to 5 lines>
<Press 'Enter' to exit> Enter line 1: test it 1
<Press 'Enter' to exit> Enter line 2: test it 2
<Press 'Enter' to exit> Enter line 3: test it 3
<Press 'Enter' to exit> Enter line 4: test it 4
<Press 'Enter' to exit> Enter line 5: test it 5

You entered ...
<001> 'test it 1'
<002> 'test it 2'
<003> 'test it 3'
<004> 'test it 4'
<005> 'test it 5'
 ===> Press 'Enter' to continue ===>

 ===> WORDS FROM 'readWord.txt' ===>
abd
def
ghi
jkl
mn
opqrst
uv
wxyz
mn
opqrst
uv
wxyz
===

 ===> VIA KEY BOARD enter up to 5 words ===>
Enter words (Enter q to quit)  : test it one two three four

You entered ...
 1: test
 2: it
 3: one
 4: two
 5: three
 ===> Press 'Enter' to exit program ===>*/
Title: Re: New thread especially for students of C and C++
Post by: David on April 12, 2010, 08:01:08 AM
And here is the promised Clist ... with a stable mergesort for the list of rec's ...

(Note: needs file readLine.h supplied above)

Code: [Select]
/* eRec's_mergesortClist.c */

/* C++ 'Clist emulation' using a C Clist struct ... (see demo MENU below) */

#include <ctype.h> /* re. tolower */

#define FILENAME "employeeRecords.txt"
#define MENU      " 1. Enter a new record. \n" \
                  " 2. Retrieve a record by name (ignore case). \n" \
                  " 3. Retrieve a record by phone number. \n" \
                  " 4. Show all records. \n" \
                  " 5. Mergesort all records by name (ignore case). \n" \
                  " 6. Mergesort all records by phone number. \n" \
                  " 7. Chop exact duplicates from records (ignore case). \n" \
                  " 8. Edit/Delete a record. \n" \
                  " 9. Write new file of sorted unique records. \n" \
                  "10. Exit \n"
#define PROMPT    "Enter a number in range 1 to 10 : "


/* Clist.h */ /* this version: 2010-05-11 */

#ifndef dwClist
#define dwClist

/* using readLine here ... instead of gets and fgets */
#include "readLine.h" /* includes stdio.h, stdlib.h  ... also myAssert( ... ) */

/* Note: stdio.h, stdlib.h and myAssert were included in "readLine.h" above */
#include <string.h> /* re. memcpy */

typedef struct eRec
{
    char* name;     /* since CStrings are '\0' terminated ... can get strlen */
    char* address;  /* ditto above ... */
    char* phone;    /* 3 digit area-code  +  7 digits  +  '\0'  at end */
    struct eRec* next;
} Dat ;

typedef Dat* pDat;

typedef struct myClist
{
    pDat start;
    int size;
    int isSorted;
} Clist;

/* with these, an address is passed, so NO copy made and/or original updated */
void init( Clist* ); /* sets start to NULL, size to 0. isSorted to 1 */
void push_front( Clist*, Dat* );
void freeAll( Clist* );
void show( pDat );
void showAll( Clist* );

void mergesortNamesIgnoreCase( Clist* );
void mergesortPhones( Clist* );
pDat mergeNamesIgnoreCase( Clist*, Clist* );
pDat mergePhones( Clist*, Clist* );
int strcmpIgnoreCase( char [], char [] );
int compareDatIgnoreCase( Dat*, Dat* );
void chopDups( Clist* );

void init( Clist* list )
{
    list->start = NULL;
    list->size = 0;
    list->isSorted = 1;
}

void push_front( Clist* list, Dat* d )
{
    pDat p = (pDat) malloc( sizeof(Dat) );
    if( p == NULL )
    {
        freeAll( list );
        myAssert( 0, "Error: malloc failed to allocate memory." );
    }

    /* now add in ALL new dat ... (assuming next pointer is last of dat) */
    memcpy( p, d, sizeof(Dat)-sizeof(pDat) ); /* -sizeof(any_pointer) is ok */
    /* and set pointers to next ... and start ...*/
    p->next = list->start;
    list->start = p;

    ++ list->size;
    if( list->size > 1 ) list->isSorted = 0;
}

void freeAll( Clist* list )
{
    if( list->start != NULL )
    {
        pDat cur = list->start;
        for( ; cur != NULL; list->start = cur )
        {
            cur = cur->next;
            free( list->start->phone );
            free( list->start->address );
            free( list->start->name );

            free( list->start );
        }
        init( list );
    }
}

void show( pDat pd )
{
    printf
    (
        "Name: %s,  Address: %s,  Phone: %s\n",
        pd->name, pd->address, pd->phone
    );
}
void showAll( Clist* list )
{
    pDat p = list->start;
    for( ; p != NULL; p = p->next ) show( p );
    printf( "Employee records on file = %d\n\n", list->size );
}

/* a recursive mergesort ... */
void mergesortNamesIgnoreCase(Clist* list)
{
    pDat cur = list->start;
    Clist a, b;

    /* base case is a Clist of length 0 or 1 ... */
    if ((cur == NULL) || (cur->next == NULL))  return;

    /* split Clist into 'a' and 'b' sublists ... */
    a.start = cur;
    b.start = cur->next;
    while((b.start != NULL) && (b.start->next != NULL))
    {
        cur = cur->next;
        b.start = b.start->next->next;
    }
    b.start = cur->next;
    cur->next = NULL; /* Clist divided into 2 roughly equal parts now ... */

    /* recursively sort the sublists ... */
    mergesortNamesIgnoreCase(&a);
    mergesortNamesIgnoreCase(&b);

    /* merge the two sorted Clists together ... */
    list->start = mergeNamesIgnoreCase(&a, &b);
    list->isSorted = 1;
}


/* merge two sorted Clists with heads 'a' and 'b' ... in sorted order */
pDat mergeNamesIgnoreCase(Clist* a, Clist* b )
{
    pDat sorted, new_merged_head;

    if( a->start == NULL ) return b->start;
    if( b->start == NULL ) return a->start;

    if( strcmpIgnoreCase( a->start->name, b->start->name ) <= 0 )
    {
        sorted = a->start;
        a->start = a->start->next;
    }
    else
    {
        sorted = b->start;
        b->start = b->start->next;
    }
    new_merged_head = sorted;

    /* now ... */
    while( a->start != NULL && b->start != NULL )
    {
        if( strcmpIgnoreCase( a->start->name, b->start->name ) <= 0 )
        {
            sorted->next = a->start;
            sorted = a->start;
            a->start = a->start->next;
        }
        else
        {
            sorted->next = b->start;
            sorted = b->start;
            b->start = b->start->next;
        }
    }

    /* and finally ... */
    if( a->start != NULL )
        sorted->next = a->start;
    else if( b->start != NULL )
        sorted->next = b->start;

    return new_merged_head;
}

/* a recursive mergesort ... */
void mergesortPhones(Clist* list)
{
    pDat cur = list->start;
    Clist a, b;

    /* base case is a Clist of length 0 or 1 ... */
    if ((cur == NULL) || (cur->next == NULL))  return;

    /* split Clist into 'a' and 'b' sublists ... */
    a.start = cur;
    b.start = cur->next;
    while((b.start != NULL) && (b.start->next != NULL))
    {
        cur = cur->next;
        b.start = b.start->next->next;
    }
    b.start = cur->next;
    cur->next = NULL; /* Clist divided into 2 roughly equal parts now ... */

    /* recursively sort the sublists ... */
    mergesortPhones(&a);
    mergesortPhones(&b);

    /* merge the two sorted Clists together ... */
    list->start = mergePhones(&a, &b);
    list->isSorted = 0;
}


/* merge two sorted Clists with heads 'a' and 'b' ... in sorted order */
pDat mergePhones(Clist* a, Clist* b )
{
    pDat sorted, new_merged_head;

    if( a->start == NULL ) return b->start;
    if( b->start == NULL ) return a->start;

    if( strcmp( a->start->phone, b->start->phone ) <= 0 )
    {
        sorted = a->start;
        a->start = a->start->next;
    }
    else
    {
        sorted = b->start;
        b->start = b->start->next;
    }
    new_merged_head = sorted;

    /* now ... */
    while( a->start != NULL && b->start != NULL )
    {
        if( strcmp( a->start->phone, b->start->phone ) <= 0 )
        {
            sorted->next = a->start;
            sorted = a->start;
            a->start = a->start->next;
        }
        else
        {
            sorted->next = b->start;
            sorted = b->start;
            b->start = b->start->next;
        }
    }

    /* and finally ... */
    if( a->start != NULL )
        sorted->next = a->start;
    else if( b->start != NULL )
        sorted->next = b->start;

    return new_merged_head;
}

/* ignore case compare function ... */
int strcmpIgnoreCase( char s1[], char s2[] )
{
    for( ; *s1 != 0 && *s2 != 0; ++s1, ++s2 )
    {
        if( tolower(*s1) != tolower(*s2) ) break;
    }
    /* ok ... check the end conditions ... */
    if( *s1 == 0  &&  *s2 == 0 ) return 0; /* strings equal, don't call tolower */
    return tolower(*s1) - tolower(*s2);
}

int compareDatIgnoreCase( Dat* x, Dat* y )
{
    int compare1 = strcmpIgnoreCase( x->name, y->name ); /* first compare names */
    if( compare1 == 0 ) /* if the same ... */
    {
        int compare2 = strcmpIgnoreCase( x->address, y->address ); /* then address */
        if( compare2 == 0 ) /* if the same ... */
            return strcmp( x->phone, y->phone ); /* then phone number ... */
        /* else ...*/
        return compare2; /* use addresses after all  ... */
    }
    /* else ... */
    return compare1; /* use names afer all ... since names were NOT the same */
}

void chopDups( Clist* list ) /* chop exact duplicates only (BUT IGNORE CASE) */
{
    pDat p, tmp;

    /* first make sure in name-sorted order */
    if(!list->isSorted) mergesortNamesIgnoreCase(list);

    p = list->start;
    while( p && p->next != NULL )
    {
        if( compareDatIgnoreCase( p, p->next ) == 0 )
        {
            tmp = p->next;
            p->next = p->next->next;
           
            /* first free dynamic memory for duplicates */
            free( tmp->name );
            free( tmp->address );
            free( tmp->phone );
           
            free( tmp );
            -- list->size;
        }
        else p = p->next; /* move up one ... */
    }
}

#endif


/* with these, an address is passed, so NO copy made and/or original updated */
void readFile( Clist* );
int showMenuGetChoice( Clist* );
int isValidPhoneNum( char [] );
pDat getNameIgnoreCase( Clist*, char [] );
pDat getNumber( Clist*, char [] );
void takeInDatAndFile( Clist* );
void writeSortedUnique( Clist* );
void editDel( Clist* );
void edit( Clist*, pDat );
void del( Clist*, pDat );
int getChoice( char [] );



int main() /* ********************* MAIN BEGINS ***************************** */
{
    int choice;
    Clist clist;
    init( &clist ); /* passing the address of clist so clist gets updated */
    readFile( &clist );
    if( clist.size == 0 )
        printf
        (
            "File %s will be created after data has been entered.\n",
            FILENAME
        );
    else showAll( &clist ); /* passing address to avoid making another copy */
   
    printf( "Now clist.start = 0x%p ... clist.size = %d\n\n",
            clist.start, clist.size );
   
    do
    {
        choice = showMenuGetChoice( &clist ); /* passing address ... */
    }
    while( choice != 10 ); /* i.e. exit on choice of 10 ...  */
   
    freeAll( &clist ); /* free all dynamic memory when done with it ... */
   
    printf( "\nNow clist.start = 0x%p ... clist.size = %d\n",
            clist.start, clist.size );

    /* if using windows ... can do this ... especially while debugging ... */
    system( "notepad " FILENAME );
    return 0;
} /* **************************** MAIN ENDS ********************************* */



void readFile( Clist* list )
{
    FILE* fp;
    if( !(fp = fopen(FILENAME, "r")) )
    {
        printf( "File %s NOT found ...\n", FILENAME );
    }
    else
    {
        Dat d;
        while
        (
            (d.name = readLine(fp)) &&
            (d.address = readLine(fp)) &&
            (d.phone = readLine(fp))
        )
        {
            push_front( list, &d );
        }
        fclose( fp );
    }
}

int showMenuGetChoice( Clist* list )
{
    int num;
    char* tmp = NULL;

    fputs( MENU, stdout );
    while
    (
        printf( PROMPT ) &&
        (tmp = readLine(stdin)) != NULL &&
        ( (num = atoi(tmp)) < 1 || num > 10 )
    )
    {
        fputs( "Out of valid range 1..10  ", stdout );
        free(tmp);
    }
    free(tmp);

    if( num == 1 ) takeInDatAndFile( list );
    else if( num == 2 )
    {
        pDat pd = NULL;
        fputs( "Enter the name to find: ", stdout );
        tmp = readLine(stdin);
        if( (pd = getNameIgnoreCase( list, tmp )) == NULL )
            printf( "%s not found.\n", tmp );
        else
            show( pd );
        free( tmp );
    }
    else if( num == 3 )
    {
        pDat pd;
        fputs( "Enter the number to find: ", stdout );
        tmp = readLine(stdin);
        if( (pd = getNumber( list, tmp )) == NULL )
            printf( "%s not found.\n", tmp );
        else
            show( pd );
        free( tmp );
    }
    else if( num == 4 ) showAll( list );
    else if( num == 5 ) mergesortNamesIgnoreCase( list );
    else if( num == 6 ) mergesortPhones( list );
    else if( num == 7 ) chopDups( list );
    else if( num == 8 ) editDel ( list );
    else if( num == 9 ) writeSortedUnique( list );
    /* else is 10 ... so will exit ... */
   
    return num;
}

/* validates that 10 char's are present and all are numeric ... */
int isValidPhoneNum( char ph[] )
{
    if( strlen(ph) != 10 ) return 0;
    for( ; *ph != 0; ++ph )
        if( *ph < '0' || *ph >'9' ) return 0;
    return 1;
}

/* returns index ... if found ... otherwise , returns -1 if NOT found */
pDat getNameIgnoreCase( Clist* Clist, char nameStr[] )
{
    pDat pd = Clist->start;
    for( ; pd != NULL; pd = pd->next )
        if( strcmpIgnoreCase(pd->name, nameStr) == 0 )
            return pd;
    return NULL;
}

pDat getNumber( Clist* Clist, char numStr[] )
{
    pDat pd = Clist->start;
    for( ; pd != NULL; pd = pd->next )
        if( strcmp(pd->phone, numStr) == 0 )
            return pd;
    return NULL;

}

void takeInDatAndFile( Clist* list )
{
    Dat d;
    FILE* pFile = fopen( FILENAME, "a" );
    myAssert( (pFile != NULL), "Error: " FILENAME " not opened." );
   
    printf( "Enter name: " );
    d.name = readLine( stdin );
    printf("Enter address: ");
    d.address = readLine( stdin );
    for( ; ; )
    {
        printf( "Enter telephone: " );
        d.phone = readLine( stdin );
        if( isValidPhoneNum(d.phone) )
        {
            break;
        }
        /* else ... */
        fputs( "Only 10 digit number valid here ... ", stdout );
        free( d.phone );
    }

    /* Now we have good data ... so file and add to vector if ok ?  */
    if( getChoice( "Ok ... (y/n) ? " ) == 'y' )
    {
        fprintf( pFile, "%s\n",  d.name );
        fprintf( pFile, "%s\n", d.address );
        fprintf( pFile, "%s\n", d.phone );
        push_front( list, &d );
        printf( "Information has been filed in file %s.\n", FILENAME );
    }
    else
    {
        puts( "Aborted ..." );
        free( d.name );
        free( d.address );
        free( d.phone );
    }

    fclose( pFile );
}

void writeSortedUnique( Clist* list )
{
    pDat pd;
    int i = 0;
    FILE* fp = fopen( "SU" FILENAME, "w" ); /* compiler concat's these TEXTs */
    myAssert( (fp != NULL), "Error: file SU" FILENAME " failed to open." );
   
    /* ok ... file is open ... so write all rec's to file ..*/
   
    chopDups( list ); /* first ... make sure sorted and unique */
    for( pd = list->start; pd != NULL; pd = pd->next )
    {
        fprintf( fp, "%s\n", pd->name );
        fprintf( fp, "%s\n", pd->address );
        fprintf( fp, "%s\n", pd->phone );
        ++i;
    }
    fclose( fp ); /* flushes buffer ... so all written to file now ... */
    printf( "\n%d records filed in file SU%s list size %d\n\n",
            i, FILENAME,  list->size );
   
    /* and if have Windows OS ... can do this ... */
    system( "notepad SU" FILENAME ); /*compiler concat's text at compile time*/
}

void editDel( Clist* list )
{
    pDat pd;
    int reply;
    char* tmp;
    for( ; ; )
    {
        reply = getChoice( "Find by Name/Phone/Abort (n/p/a) ? " );
        if( reply == 'n' )
        {
            fputs( "Enter the name to find: ", stdout );
            tmp = readLine(stdin);
            if( (pd = getNameIgnoreCase( list, tmp )) == NULL )
                printf( "%s not found.\n", tmp );
            else
            {
                show( pd );
                reply = getChoice( "Edit/Delete/Abort (e/d/a) ? " );
                if( reply == 'e' ) edit( list, pd );
                else if( reply == 'd' ) del( list, pd);
                else if( reply == 'a' )
                {
                    puts( "Ok, edit/delete aborted ..." );
                    free( tmp );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'p' )
        {
            fputs( "Enter the number to find: ", stdout );
            tmp = readLine(stdin);
            if( (pd = getNumber( list, tmp )) == NULL )
                printf( "%s not found.\n", tmp );
            else
            {
                show( pd );
                reply = getChoice( "Edit/Delete/Abort (e/d/a) ? " );
                if( reply == 'e' ) edit( list, pd );
                else if( reply == 'd' ) del( list, pd );
                else if( reply == 'a' )
                {
                    puts( "Ok, edit/delete aborted ..." );
                    free( tmp );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'a' )
        {
            puts( "Ok, edit/delete aborted ..." );
            break;
        }
        else fputs( "Only n/p/a are valid ... ", stdout );
    }
}

void edit( Clist* list, pDat pd )
{
    Dat d;
    for( ; ; )
    {
        printf( "Enter name: " );
        d.name = readLine( stdin );
        printf("Enter address: ");
        d.address = readLine( stdin );
        for( ; ; )
        {
            printf( "Enter telephone: " );
            d.phone = readLine( stdin );
            if( isValidPhoneNum(d.phone) ) break; /* out of INNER for loop ...*/
            /* else ... */
            fputs( "Only 10 digit number valid here ... ", stdout );
            free( d.phone );
        }
        /* ok ... we have some new data ... */
        if( getChoice( "Ok ... (y/n) ? " ) == 'y' )
        {
            /* then edit ... but first free old ...  */
            free( pd->name );
            free( pd->address );
            free( pd->phone );

            pd->name =d.name;
            pd->address =d.address;
            pd->phone =d.phone;

            list->isSorted = 0;
            break;
        }
        else
        {
            puts( "Aborted ..." );
            free( d.name );
            free( d.address );
            free( d.phone );
            break;
        }
    }
}

void del( Clist* list, pDat pd )
{
    for( ; ; )
    {
        int reply = getChoice( "Really delete/abort (d/a) ? " );
        if( reply == 'a' )
        {
            puts( "Delete aborted ..." );
            break;
        }
        else if( reply == 'd' )
        {
            if( pd == list->start ) list->start = list->start->next;
            else
            {
                pDat prev = list->start;
                for( ; prev->next != pd; prev = prev->next ) ;
                prev->next = prev->next->next;
            }
            /* first free dynamic memory */
            free( pd->name );
            free( pd->address );
            free( pd->phone );
           
            free( pd );
            -- list->size;
            break;
        }
        /* else ... */
        fputs( "Enter 'd' and WILL delete ... or 'a' to abort ... \n", stdout );
    }
   
    if( list->size < 2 ) list->isSorted = 1;
}

int getChoice( char text[] )
{
    int c, reply;
    fputs( text, stdout );
    c = reply = tolower( getchar() );
    while( c != '\n' ) c = getchar(); /* flush stdin ... */
    return reply;
}
Title: Re: New thread especially for students of C and C++
Post by: David on April 12, 2010, 08:03:06 AM
Here are the remaining function definitions used by the above program ...


Code: [Select]
void readFile( Clist* list )
{
    FILE* fp;
    if( !(fp = fopen(FILENAME, "r")) )
    {
        printf( "File %s NOT found ...\n", FILENAME );
    }
    else
    {
        Dat d;
        while
        (
            (d.name = readLine(fp)) &&
            (d.address = readLine(fp)) &&
            (d.phone = readLine(fp))
        )
        {
            push_front( list, &d );
        }
        fclose( fp );
    }
}

int showMenuGetChoice( Clist* list )
{
    int num;
    char* tmp = NULL;

    fputs( MENU, stdout );
    while
    (
        printf( PROMPT ) &&
        (tmp = readLine(stdin)) != NULL &&
        ( (num = atoi(tmp)) < 1 || num > 10 )
    )
    {
        fputs( "Out of valid range 1..10  ", stdout );
        free(tmp);
    }
    free(tmp);

    if( num == 1 ) takeInDatAndFile( list );
    else if( num == 2 )
    {
        pDat pd = NULL;
        fputs( "Enter the name to find: ", stdout );
        tmp = readLine(stdin);
        if( (pd = getNameIgnoreCase( list, tmp )) == NULL )
            printf( "%s not found.\n", tmp );
        else
            show( pd );
        free( tmp );
    }
    else if( num == 3 )
    {
        pDat pd;
        fputs( "Enter the number to find: ", stdout );
        tmp = readLine(stdin);
        if( (pd = getNumber( list, tmp )) == NULL )
            printf( "%s not found.\n", tmp );
        else
            show( pd );
        free( tmp );
    }
    else if( num == 4 ) showAll( list );
    else if( num == 5 ) mergesortNamesIgnoreCase( list );
    else if( num == 6 ) mergesortPhones( list );
    else if( num == 7 ) chopDups( list );
    else if( num == 8 ) editDel ( list );
    else if( num == 9 ) writeSortedUnique( list );
    /* else is 10 ... so will exit ... */
    
    return num;
}

/* validates that 10 char's are present and all are numeric ... */
int isValidPhoneNum( char ph[] )
{
    if( strlen(ph) != 10 ) return 0;
    for( ; *ph != 0; ++ph )
        if( *ph < '0' || *ph >'9' ) return 0;
    return 1;
}

/* returns index ... if found ... otherwise , returns -1 if NOT found */
pDat getNameIgnoreCase( Clist* Clist, char nameStr[] )
{
    pDat pd = Clist->start;
    for( ; pd != NULL; pd = pd->next )
        if( strcmpIgnoreCase(pd->name, nameStr) == 0 )
            return pd;
    return NULL;
}

pDat getNumber( Clist* Clist, char numStr[] )
{
    pDat pd = Clist->start;
    for( ; pd != NULL; pd = pd->next )
        if( strcmp(pd->phone, numStr) == 0 )
            return pd;
    return NULL;

}

void takeInDatAndFile( Clist* list )
{
    Dat d;
    FILE* pFile = fopen( FILENAME, "a" );
    myAssert( (pFile != NULL), "Error: " FILENAME " not opened." );
    
    printf( "Enter name: " );
    d.name = readLine( stdin );
    printf("Enter address: ");
    d.address = readLine( stdin );
    for( ; ; )
    {
        printf( "Enter telephone: " );
        d.phone = readLine( stdin );
        if( isValidPhoneNum(d.phone) )
        {
            break;
        }
        /* else ... */
        fputs( "Only 10 digit number valid here ... ", stdout );
        free( d.phone );
    }

    /* Now we have good data ... so file and add to vector if ok ?  */
    if( getChoice( "Ok ... (y/n) ? " ) == 'y' )
    {
        fprintf( pFile, "%s\n",  d.name );
        fprintf( pFile, "%s\n", d.address );
        fprintf( pFile, "%s\n", d.phone );
        push_front( list, &d );
        printf( "Information has been filed in file %s.\n", FILENAME );
    }
    else
    {
        puts( "Aborted ..." );
        free( d.name );
        free( d.address );
        free( d.phone );
    }

    fclose( pFile );
}

void writeSortedUnique( Clist* list )
{
    pDat pd;
    int i = 0;
    FILE* fp = fopen( "SU" FILENAME, "w" ); /* compiler concat's these TEXTs */
    myAssert( (fp != NULL), "Error: file SU" FILENAME " failed to open." );
    
    /* ok ... file is open ... so write all rec's to file ..*/
    
    chopDups( list ); /* first ... make sure sorted and unique */
    for( pd = list->start; pd != NULL; pd = pd->next )
    {
        fprintf( fp, "%s\n", pd->name );
        fprintf( fp, "%s\n", pd->address );
        fprintf( fp, "%s\n", pd->phone );
        ++i;
    }
    fclose( fp ); /* flushes buffer ... so all written to file now ... */
    printf( "\n%d records filed in file SU%s list size %d\n\n",
            i, FILENAME,  list->size );
    
    /* and if have Windows OS ... can do this ... */
    system( "notepad SU" FILENAME ); /*compiler concat's text at compile time*/
}

void editDel( Clist* list )
{
    pDat pd;
    int reply;
    char* tmp;
    for( ; ; )
    {
        reply = getChoice( "Find by Name/Phone/Abort (n/p/a) ? " );
        if( reply == 'n' )
        {
            fputs( "Enter the name to find: ", stdout );
            tmp = readLine(stdin);
            if( (pd = getNameIgnoreCase( list, tmp )) == NULL )
                printf( "%s not found.\n", tmp );
            else
            {
                show( pd );
                reply = getChoice( "Edit/Delete/Abort (e/d/a) ? " );
                if( reply == 'e' ) edit( list, pd );
                else if( reply == 'd' ) del( list, pd);
                else if( reply == 'a' )
                {
                    puts( "Ok, edit/delete aborted ..." );
                    free( tmp );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'p' )
        {
            fputs( "Enter the number to find: ", stdout );
            tmp = readLine(stdin);
            if( (pd = getNumber( list, tmp )) == NULL )
                printf( "%s not found.\n", tmp );
            else
            {
                show( pd );
                reply = getChoice( "Edit/Delete/Abort (e/d/a) ? " );
                if( reply == 'e' ) edit( list, pd );
                else if( reply == 'd' ) del( list, pd );
                else if( reply == 'a' )
                {
                    puts( "Ok, edit/delete aborted ..." );
                    free( tmp );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'a' )
        {
            puts( "Ok, edit/delete aborted ..." );
            break;
        }
        else fputs( "Only n/p/a are valid ... ", stdout );
    }
}

void edit( Clist* list, pDat pd )
{
    Dat d;
    for( ; ; )
    {
        printf( "Enter name: " );
        d.name = readLine( stdin );
        printf("Enter address: ");
        d.address = readLine( stdin );
        for( ; ; )
        {
            printf( "Enter telephone: " );
            d.phone = readLine( stdin );
            if( isValidPhoneNum(d.phone) ) break; /* out of INNER for loop ...*/
            /* else ... */
            fputs( "Only 10 digit number valid here ... ", stdout );
            free( d.phone );
        }
        /* ok ... we have some new data ... */
        if( getChoice( "Ok ... (y/n) ? " ) == 'y' )
        {
            /* then edit ... but first free old ...  */
            free( pd->name );
            free( pd->address );
            free( pd->phone );

            pd->name =d.name;
            pd->address =d.address;
            pd->phone =d.phone;

            list->isSorted = 0;
            break;
        }
        else
        {
            puts( "Aborted ..." );
            free( d.name );
            free( d.address );
            free( d.phone );
            break;
        }
    }
}

void del( Clist* list, pDat pd )
{
    for( ; ; )
    {
        int reply = getChoice( "Really delete/abort (d/a) ? " );
        if( reply == 'a' )
        {
            puts( "Delete aborted ..." );
            break;
        }
        else if( reply == 'd' )
        {
            if( pd == list->start ) list->start = list->start->next;
            else
            {
                pDat prev = list->start;
                for( ; prev->next != pd; prev = prev->next ) ;
                prev->next = prev->next->next;
            }
            /* first free dynamic memory */
            free( pd->name );
            free( pd->address );
            free( pd->phone );
            
            free( pd );
            -- list->size;
            break;
        }
        /* else ... */
        fputs( "Enter 'd' and WILL delete ... or 'a' to abort ... \n", stdout );
    }
    
    if( list->size < 2 ) list->isSorted = 1;
}

int getChoice( char text[] )
{
    int c, reply;
    fputs( text, stdout );
    c = reply = tolower( getchar() );
    while( c != '\n' ) c = getchar(); /* flush stdin ... */
    return reply;
}

You might liked to see this updated version, with separated out files ... Clist.h and Clist_func's.h (that uses function pointers) ... to facilitate reuse.

http://developers-heaven.net/forum/index.php/topic,2583.0.html
Title: Re: New thread especially for students of C and C++
Post by: David on April 12, 2010, 08:22:56 AM
Just some reserved space here ... for future updates/additions ...

like this:

Code: [Select]
/*
    2.  C program to find minimum, maximum, average ... of a 'C vector' style
        dynamic array of doubles. (Note the input validation for each double.)
    
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

/* Cvect.h */ /* this version 2010-04-14 */

#ifndef dw1CVECTOR_H
#define dw1CVECTOR_H

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

#define CHUNK_SIZE 5 /* increase/re-set this to match your data size */

typedef struct myDat
{
    double val;
} Dat ;

typedef struct myCvec
{
    Dat* ary;
    int size;
    int cap; /* capacity */
} Cvec ;

void init( Cvec* );
void push_back( Cvec* ad, Dat* d );
void resize( Cvec* ad );
void freeAll( Cvec* ad );
void myAssert( int condition, char text[] );

void init( Cvec* ad )
{
    ad->ary = NULL;
    ad->size = ad->cap = 0;
}

void push_back( Cvec* ad, Dat* d )
{
    if( ad->size == ad->cap ) resize( ad );
    /* now add in new dat ... */
    memcpy( &(ad->ary[ad->size]), d, sizeof(Dat) );
    ++ ad->size;
}

/* new array to hold one 'chunk' more records than before, copies old to new */
void resize( Cvec* ad )
{
    Dat* p;
    ad->cap += CHUNK_SIZE;
    p = (Dat*) realloc( ad->ary, ad->cap * sizeof(Dat) );
    if( p == NULL )
    {
        freeAll( ad );
        myAssert( 0, "Error: calloc failed to allocate memory." );
    }
    /* else ... */
    ad->ary = p; /* update ... the base address of our ad->ary ... */
}

void freeAll( Cvec* ad )
{
    if( ad->ary == NULL ) return;
    /*
    int i;
    for( i = ad->size-1; i >= 0; --i )
    {
        free(...); // pointer to any dynamic memory in struct myDat
        ...
        free(...);
    }
    */
    free( ad->ary );
    ad->ary = NULL;
    ad->size = ad->cap = 0; /* update ... */
}

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

#endif



#define HEADER "C program to find minimum, maximum, average " \
               "of a 'C vector' of doubles ...\n"
              
void getInfoRe( Cvec* ad,  double* min, double* max, double* avg );
int more( void );


int main( void ) /* ********************************************************* */
{
    int i = 0; /* i is used below as an index counter ... */
    double max, min, avg; /* variables to hold info returned by 'ref' */
    Cvec v;
    init( &v ); /* Note: MUST initial Cvec v ... for Cvec to work */
    
    puts( HEADER );
    for( ; ; )
    {
        Dat d;
        int numGoodVars;
        printf( "Observation %-3d: ", i+1 );
        numGoodVars = scanf( "%lf", &d.val );
        while( getchar() != '\n' ) ; /* flush all char's in stdin stream */ ;
        if( numGoodVars != 1 ) puts( "Numbers only please ..." );
        else
        {
            push_back( &v, &d ); /* since good data was obtained */
            if( !more() ) break;
            /* else ...*/
            ++i;
        }
    }

    puts( "\n You entered ...\n" );
    for( i = 0; i < v.size; ++i )
    {
        printf( "%-3d: ", i+1 ); /* left justify in a 3 char wide field */
        printf( "%f\n", v.ary[i].val ); /* Note: 'f' here handles double and float */
    }

    /* Note: 'addresses' are passed here ... to receiving pointer variables */
    getInfoRe( &v, &min, &max, &avg );
    printf( "\n Min: %f  Max: %f  Average: %f\n", min, max, avg );
    
    printf( "\n Before freeAll: v.size = %d, v.cap = %d\n", v.size, v.cap );
    freeAll( &v );
    printf( "\n After  freeAll: v.size = %d, v.cap = %d\n", v.size, v.cap );
    
    fputs( "\n Press 'Enter' to continue ... ", stdout );
    getchar();
    return 0;
} /* ************************************************************************ */


/* note where we are catching 'addresses' .... and note inside ... */
/* we have to use (i.e. dereference) the value at that address by using *var  */
void getInfoRe( Cvec* ad, double* min, double* max, double* avg )
{
    int i;
    double sum = *min = *max = ad->ary[0].val; /* to start, use this actual value */
    for( i = 1; i < ad->size; ++i ) /* start at 1 since already used 0 above */
    {
        sum += ad->ary[i].val;
        if( ad->ary[i].val < *min ) *min = ad->ary[i].val; /* update ... */
        if( ad->ary[i].val > *max ) *max = ad->ary[i].val;
    }
    /* when we reach ... we have max, min and sum ... So can find average */
    *avg = sum/ad->size; /*  Note:  double/int ... yields a double */
}

int more( void )
{
    int c, reply;
    fputs( "More (y/n) ? ", stdout );
    reply = c = getchar();
    while( c != '\n' ) c = getchar(); /* flush stdin ... */
    return !(reply=='n' || reply=='N');
}


You may like to compare the above 'push_back vector version' with the below version that uses just a plain dynamic array ...

Code: [Select]
/*
    1.  C program to find minimum, maximum, average ... of a dynamic
        array of doubles.  (Note the 2 input validation loops.)
    
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

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

#define HEADER "C program to find minimum, maximum, average " \
               "of a dynamic array of doubles ...\n"

void getInfoRe(double* dAry, int size, double* min, double* max, double* avg);

int main()
{
    int i, numObservations; /* i is used below as an index counter ... */
    double* dAry; /* a pointer variable to hold an address of a double */
    double max, min, avg; /* variables to hold info returned by 'ref' */

    puts( HEADER );
    for( ; ; ) /* a C forever loop ... until break ... */
    {
        int numGoodVars;
        printf( "Please enter the number of observations: " );
        numGoodVars = scanf( "%d", &numObservations );
        while( getchar() != '\n' ) ; /* flush stdin ... */
        if( numGoodVars != 1 || numObservations < 1 )
            puts( "Only int's > 0 allowed here ..." );
        else { putchar('\n'); break; }
    }
    
    /* when we reach here, we have a good 'int' above ... so get new memory */
    dAry = (double*) malloc( sizeof(double) * numObservations );
    if( dAry == NULL )
    {
        fputs( "Error: malloc failed to allocate memory. "
               "Press 'Enter' to exit ... ", stderr );
        getchar();
        exit(1);
    }

    /* dAry holds the address of the start of memory to hold numObservations */
    for( i = 0; i < numObservations ; ) /* i updated below ONLY if good data */
    {
        int numGoodVars;
        printf( "Observation %-3d : ", i+1 );
        numGoodVars = scanf( "%lf", &dAry[i] ); /* recall dAry holds 'double's */
        while( getchar() != '\n' ) ; /* flush all char's in stdin stream */ ;
        if( numGoodVars != 1 ) puts( "Numbers only please ..." );
        else ++i; /* since good data was obtained above ... now update i */
    }

    puts( "\nYou entered ...\n" );
    for( i = 0; i < numObservations; ++i )
    {
        printf( "%-3d : ", i+1 ); /* left justify in a 3 char wide field */
        printf( "%f\n", dAry[i] ); /* Note: 'f' here handles double and float */
    }

    /* Note 1: dAry below is already an 'address' to the array of doubles */
    /* Note 2: 'addresses' are passed here ... to receiving pointer variables */
    getInfoRe( dAry, numObservations, &min, &max, &avg );

    printf( "\nMin: %f  Max: %f  Average: %f\n", min, max, avg );

    fputs( "\nPress 'Enter' to continue ... ", stdout );
    getchar();
    return 0;
}

/* note where we are catching 'addresses' .... and note inside ... */
/* we have to use (i.e. dereference) the value at that address by using *var  */
void getInfoRe(double* dAry, int size, double* min, double* max, double* avg)
{
    int i;
    double sum = *min = *max = dAry[0]; /* to start, use this actual value */
    for( i = 1; i <size; ++i ) /* start at 1 since already used 0 above */
    {
        sum += dAry[i]; /* sum = sum + dAry[i] */
        if( dAry[i] < *min ) *min = dAry[i]; /* update ... if applies */
        if( dAry[i] > *max ) *max = dAry[i];
    }
    /* when we reach ... we have max, min and sum ... So can find average */
    *avg = sum/size; /*  Note:  double/int ... yields a double */
}


And an updated Cvec version ... with the Cvec.h in its own separate file ... to facilitate reuse ...

http://developers-heaven.net/forum/index.php/topic,466.msg674.html#msg674

And below is that Cvec.h file used by the program in the above link ...

http://developers-heaven.net/forum/index.php/topic,466.msg673.html#msg673
Title: Re: New thread especially for students of C and C++
Post by: David on April 16, 2010, 06:24:08 PM
Here is a demo, using C++, of recursion in a simple List of char ...

Code: [Select]
// listClass_withReverseCharsNew.cpp

// demo of recursion with a C++ class List (of char) ...
// this version 2010-04-16

#include <iostream>

using namespace std;

// http://developers-heaven.net/forum/index.php/topic,46.0.html
// http://www.dreamincode.net/code/snippet5082.htm

class List
{
public:
    List();
    ~List();
    void append( char );
    int size() { return count; }
    // Thanks to baavgai at DIC for this 'bool' switch idea used here ...
    // so now can easily access private data member 'head'
    // for this demo display function (of recursive reverse display of a list)
    void display( bool reverse = false ) const // defaults to normal display
    {
        if( reverse ) print_rev( head ); // so can HERE pass private member head
        else print();
    }
private:
    struct Node
    {
        char data;
        Node *link;
    } *head, *tail; // pointers to head and tail of list of Nodes's (of char)
    int count;
    void print() const;
    void print_rev( Node* ) const;
};

List::List()
{
     head = tail = NULL;
     count = 0;
}


List::~List()
{
    for( Node *cur = head; cur != NULL; head = cur )
    {
        cur = head->link;
        delete head;
    }
    tail = head; // now head, tail are NULL
    count = 0;
}

void List::append( char c )
{
    Node *n = new Node;
    n->data = c;
    n->link = NULL;
   
    if( head != NULL ) // for ALL cases EXCEPT special case of first Node ...
    {
        tail->link = n;
        tail = n;
    }
    else // special case of first node handled here ...
    {
        head = tail = n;
    }
    ++count;
}

void List::print() const
{
    for( Node *cur = head; cur != NULL; cur = cur->link )
        cout << cur->data;
}

// Warning: the number of recursive calls here is size ...
// Thanks to baavgai at DIC for this idea suggested at DIC here ...
// http://www.dreamincode.net/forums/index.php?showtopic=156039&pid=927868&st=0&#entry927868
void List::print_rev( Node *cur ) const // Note: head is passed in to cur ...
{
    if( cur == NULL ) return;
    Node *tmp = cur; // push new tmp value onto stack each (recursive) call
    cur = cur->link; // advance cur value ... before recursive call ...
    print_rev( cur );

    cout << tmp->data; // use/take (this next) tmp value from (new top of) stack
}



int main()
{
    List myList;
    cout << "The list size right after construction is " << myList.size() << endl;
   
    char test[] = "abcdefghijklmnopqrstuvwxyz"; // Note: appends '\0' to end
   
    for( int i = 0; test[i] != 0; ++i ) myList.append( test[i] );
       
    cout << "The list size after appending a..z is " << myList.size() << endl;
    cout << "The list displayed: '";
    myList.display();
    cout << "'" << endl;

    cout << "Displayed reversed: '";
    myList.display( true );
    cout << "'" << endl;

    cout << "The list displayed: '";
    myList.display();
    cout << "'" << endl;

    cout << "Displayed reversed: '";
    myList.display( true );
    cout << "'" << endl;

    cout<< "Press 'Enter' to continue ... " << flush;
    cin.get();
}
Title: Re: New thread especially for students of C and C++
Post by: David on April 16, 2010, 06:40:55 PM
A simple template class List ...

Code: [Select]
// templateClassList1.cpp

// this version: 2010-04-16

// template class List with 'pFront' ... and 'pRear' and 'size' class members,
// to ease push_back ... and size requests

#include <iostream>
#include <fstream>

#define haveFile true

using namespace std;


template< class T >
class List
{
public:
    List();
    ~List();
    void push_front( T e );
    void push_back( T e );
    void add_at_index( int c, T e );
    void remove( T e );
    void show_all() const;
    bool exist( T e ) const;
    int length() const;
    T List<T>::max() const;
    T List<T>::min() const;
private:
    struct Node
    {
        T data;
        Node* next;
    }*pFront, *pRear; // pointers to 'front' and 'rear' Nodes
    int size; // number of Nodes
};

template< class T >
List<T>::List()
{
    pFront = pRear = NULL;
    size = 0;
}

template< class T >
List<T>::~List()
{
    if( !size ) return;
    for( Node* q = pFront; q != NULL; pFront = q )
    {
        q = pFront->next; // keep a copy of 'next' address in q
        delete pFront;  // delete this moving front
        // Now, via 'for loop', update moving front to 'next' (saved in q)
    }
    pRear = NULL; // Note: pFront was set to NULL above on last for loop
    size = 0;
}

template< class T >
void List<T>::push_front(T e) // inserts e with type T at front of List
{
    Node* n = new Node;
    n->data = e;
    n->next = pFront;
    pFront = n;

    // if size is 0, this is the first added element, so update rear
    if( !size ) pRear = pFront;
    ++ size; // update size ...
}

template< class T >
void List<T>::push_back(T e) // inserts e with type T at end of List
{
    Node* n = new Node;
    n->data = e;
    n->next = NULL;

    if( size ) // add after 'rear' ...
    {
        pRear->next = n; // update 'rear' ...
        pRear = n;
    }
    else // handle special case of empty list ...
    {
        pRear = pFront = n ;
    }
    
    ++ size; // update 'size' ...
}

template< class T >
void List<T>::add_at_index( int c, T e) // inserts e with type T at index c
{
    if( c >= 0 && c < size )
    {
        if( c > 0 )
        {
            // insert at index c  ... BUT after index 0 ... i.e. at 1 to size
            Node* prev = pFront;
            for( int i = 1; i < c; ++i ) prev = prev->next;

            Node* n = new Node;
            n->data = e;

            // now insert n at index c ... A -> B  becomes A->C->B
            n->next = prev->next;  // C -> B
            prev->next = n; // A -> C
            ++ size;
        } // here c is 0 ... so ...
        else push_front( e ); // resets size, pFront and pRear ... as applicable
    }
    else if( c == size ) push_back( e );
    else
    {
        cout << "\nHighest new index allowed is " << size
             << ". You requested index " << c << " ... push_back y/n ? ";
        int reply = cin.get();
        cin.sync();
        if( reply == 'n' || reply =='N' )
            return;

        // if reach hear ...
        cout <<"Ok ... will push_back ...\n";
        push_back( e );  // resets size, pFront and pRear  ... as applicable
    }
}

template< class T >
void List<T>::remove( T e )
{
    if( !size )
    {
        cout << "\nCan not remove '" << e << "' since list is empty.\n";
        return;
    }

    Node* q = pFront;
    if( q->data == e ) // if at front ...
    {
        pFront = q->next;
        delete q;
        --size;
        cout<<"\n'" << e <<"' removed ...";
        if( size <= 1 ) pRear = pFront;
        return;
    }

    // if reach here ...  'e' is NOT the first
    Node* prev = q;
    q = q->next; // q now points to 2nd node in list ...
    while( q != NULL ) // Note: also handles case of just one Node in list
    {
        if( q->data == e )
        {
            prev->next = q->next;
            delete q;
            cout<<"\n'" << e <<"' removed ...";
            --size;
            if( prev->next == NULL ) pRear = prev; // update 'rear'  ...
            return;
        }

        // if reach here ... advance both nodes up the list  ...
        prev = q;
        q = q->next;
    }
    cout<<"\n'" << e <<"' NOT found ...";
}

template< class T >
void List<T>::show_all() const
{
    for( Node* q = pFront; q != NULL; q = q->next )
        cout << endl << q->data;

    cout << "\nPress 'Enter' to continue ... ";
    cin.clear();
    cin.sync();
    cin.get();
}

template <class T>
bool List<T>::exist( T e ) const
{
    for( Node* q = pFront; q != NULL; q = q->next )
        if( q->data == e )
            return true;
    return false;
}

template <class T>
int List<T>::length() const
{
    return size;
}

template <class T>
T List<T>::max() const
{
    if( !size )
    {
        cout << "List is empty ... returning 'zero' ... ";
        return T(0);
    }
    T max = pFront->data;
    for( Node* q = pFront; q->next != NULL; q = q->next )
        if( q->next->data > max )
            max = q->next->data;
    return max;
}

template <class T>
T List<T>::min() const
{
    if( !size )
    {
        cout << "List is empty ... returning 'zero' ... ";
        return T(0);
    }
    T min = pFront->data;
    for( Node* q = pFront; q->next != NULL; q = q->next )
        if( q->next->data < min )
            min = q->next->data;
    return min;
}



int main() ////////////////////////// MAIN /////////////////////////////////////
{
    List <int> ml;
    ml.remove(1);
    cout<<"Number of elements = "<<ml.length();
    ml.show_all();

    cout << "\nml.max() = ";
    cout << ml.max();
    cout << "\nml.min() = ";
    cout << ml.max();

    ml.push_back(4);
    ml.push_back(6);
    ml.push_front(3);
    ml.push_front(2);
    ml.push_front(1);
    cout << "\n\nAfter push_back(...) 's ...";
    cout<<"\n'1' is in the list = "<<(ml.exist(1) ? "True.":"False.");
    cout<<"\n'0' is in the list = "<<(ml.exist(0) ? "True.":"False.");
    cout<<"\nMax of elements = " <<ml.max();
    cout<<"\nMin of elements = " <<ml.min();
    cout<<"\nNumber of elements = "<<ml.length();
    ml.show_all();

    ml.add_at_index(0,0);
    ml.push_back(7);
    ml.add_at_index(5,5);
    ml.add_at_index(8,8);
    cout<<"\nNumber of elements = "<<ml.length();
    ml.show_all();

    ml.remove(8);
    ml.remove(7);
    ml.remove(-99);
    ml.remove(0);
    ml.remove(10);
    cout<<"\nNumber of elements = "<<ml.length();
    ml.show_all();
    
    ml.add_at_index(0,-1);
    ml.add_at_index(1,0);
    ml.add_at_index(8,7);
    ml.add_at_index(9,8);
    cout<<"\nNumber of elements = "<<ml.length();
    ml.show_all();
    

# if haveFile
   //=========================================================================//

    ifstream fin("int.dat");

    List < int > list;
    int x;
    while(fin>>x)
    {
        if(!list.exist(x))
            list.push_back(x);
    }
    cout << "\nNEW List: ";
    cout << "Number of elements = "<<list.length();
    list.show_all();

    list.remove(-47);
    list.push_front(1492);
    list.push_back(-23);
    while( list.exist(1492) ) list.remove(1492);
    while( list.exist(-23) ) list.remove(-23);
    list.remove(-1);
    if( list.exist(-2) ) list.remove(-2);
    list.remove(1926);
    list.remove(1935);
    cout << "\nNEW List Max = " << list.max();
    cout << "\nNEW List Min = " << list.min();
    cout << "\nNumber of elements = "<<list.length();
    list.show_all();
#endif

} //////////////////////////////////// END MAIN ////////////////////////////////

// FILE NAME: int.dat //
/*
1492
1776
1860
-23
1698
1935
1926
1953
1960
1492
1776
1860
-23
1698
2000
1935
1926
1953
1960
-47
*/


And for a ...

template class List ... with insert sort and merge sort (recursive) ... see:

http://developers-heaven.net/forum/index.php/topic,310.0.html
Title: Re: New thread especially for students of C and C++
Post by: David on April 16, 2010, 08:08:35 PM
Back to C ... (oops not quite yet ... but to 'C' merging two unsorted files into one new file (in sorted order) ... via insert_sort and one linked list in RAM ...... click on link below ... )

http://developers-heaven.net/forum/index.php/topic,106.msg581.html#msg581
Title: Re: New thread especially for students of C and C++
Post by: David on April 24, 2010, 08:54:09 AM
Here are some snippets for C++ strings that you may like to have added to your library ...



// default delimiter string is "\t " ( i.e. tab or space)  ... returns a C++ STL list of strings

list < string > split( const string& s, const string delimits = "\t " );

(
   Note: I hope to soon have a C version for splitting a C string into a returned list of tokens ...
   EDIT: NOW DONE! see ...
   http://developers-heaven.net/forum/index.php/topic,106.msg591.html#msg591
   
   Also ... there is a C version of these strip/trim functions ... following ...
)


And ... with same defaults as above ... ( i.e. tab or space) ...

trim or strip( someString, delimitsStr ), lstrip( someString, delimitsStr ), rstrip( someString, delimitsStr )


Code: [Select]
#include <iostream>
#include <string>
#include <list>

// this version 2010-04-28

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

using namespace std;

#define trim strip
#define ltrim lstrip
#define rtrim rstrip

// trim leading and trailing whitespaces ...
string strip( const string& s, const string t = " \t" )
{
    size_t p1 = s.find_first_not_of( t );
    if( string::npos != p1  ) // ok ... not all ws or empty ... so can safely
    {
        size_t p2 = s.find_last_not_of( t ); // get index of 'last char' ...
        return s.substr( p1, p2+1-p1 );
    }
    // else ... all whitespaces or empty ... so return an empty string
    return "";
}

// trim leading whitespaces only ...
string lstrip( const string& s, const string t = " \t" )
{
    size_t p1 = s.find_first_not_of( t );
    if( string::npos != p1 )
        return s.substr( p1 );
    // else ...
    return "";
}

// trim trailing whitespaces only ...
string rstrip( const string& s, const string t = " \t" )
{
    size_t p2 = s.find_last_not_of( t );
    if( string::npos != p2 )
        return s.substr( 0, p2+1 );
    // else ...
    return "";
}

list < string > split( const string& s, const string delimits = "\t " )
{
    list < string > tmp;
    size_t p1, p2 = 0;
    for( ; ; ) // loop forever ... until break
    {
        p1 = s.find_first_not_of( delimits, p2 ); // Note: p2 is 0 on first loop
        if( string::npos == p1 ) break; // i.e. if empty or all delimits

        p2 = s.find_first_of( delimits, p1+1 );
        if( string::npos != p2 ) // i.e. if still more ... p2 is not past end
            tmp.push_back( s.substr( p1, p2-p1 ) );
        else
        {
            tmp.push_back( s.substr( p1 ) );
            break;
        }
    }
    return tmp;
}


int main()
{
    string s = "\t   leading and trailing whitespaces\t   ";
    string t = s;
    
    cout << "NO strip: '" << s <<"'\n"
         << "   strip: '" << strip( s ) <<"'\n"
         << "  rstrip: '" << rstrip( s ) <<"'\n"
         << "  lstrip: '" << lstrip( s ) <<"'\n";
        
    s = " \t  \t  \t  ";
    
    cout << "NO strip: '" << s <<"'\n"
         << "   strip: '" << trim( s ) <<"'\n"
         << "  rstrip: '" << rtrim( s ) <<"'\n"
         << "  lstrip: '" << ltrim( s ) <<"'\n";


    list < string > mylist = split( t ); // testing split with default delimiters
    cout << "\n\nmylist.size() = "<< mylist.size() << endl;
    
    list <string > :: iterator it;
    int i = 0;
    for( it = mylist.begin(); it != mylist.end(); ++ it, ++ i)
        cout << i << ": " << *it << endl;
        
    cout << "\nPress 'Enter' to continue ... " << flush;
    cin.get();
}
Title: Re: New thread especially for students of C and C++
Post by: David on April 27, 2010, 05:03:24 AM
Or ... trim/strip the passed in string itself (trimmed string returned by reference)


Code: [Select]
#include <iostream>
#include <string>
#include <list>

// this version 2010-04-28

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

using namespace std;

#define trim strip
#define ltrim lstrip
#define rtrim rstrip

// trim leading and trailing whitespaces ...
void strip( string& s, const string t = " \t" )
{
    size_t p1 = s.find_first_not_of( t ); // get index of 'first char' ...
    if( string::npos != p1  ) // ok ... not all ws or empty ... so can safely
    {
        s.erase( 0, p1);
        size_t p2 = s.find_last_not_of( t ); // get index of 'last char' ...
        s.erase( p2+1 );
    }
    else // ... all whitespaces or empty
        s.clear();
}

// trim trailing whitespaces only ...
void rstrip( string& s, const string t = " \t" )
{
    size_t p2 = s.find_last_not_of( t );
    if( string::npos != p2 )
        s.erase( p2+1 );
    else
        s.clear();
}

// trim leading whitespaces only ...
void lstrip( string& s, const string t = " \t" )
{
    size_t p1 = s.find_first_not_of( t );
    if( string::npos != p1 )
        s.erase( 0, p1 );
    else
        s.clear();
}

void split( list<string>& lst, const string& s, const string delimits = "\t " )
{
    size_t p1, p2 = 0;
    for( ; ; ) // loop forever ... until break
    {
        p1 = s.find_first_not_of( delimits, p2 ); // Note: p2 is 0 on first loop
        if( string::npos == p1 ) break; // i.e. if empty or all delimits

        p2 = s.find_first_of( delimits, p1+1 );
        if( string::npos != p2 ) // i.e. if still more ... p2 is not past end
            lst.push_back( s.substr( p1, p2-p1 ) );
        else
        {
            lst.push_back( s.substr( p1 ) );
            break;
        }
    }
}


int main()
{
    string s, t = "\t   leading and trailing whitespaces\t   ";
    s = t;
    cout << "NO strip: '" << s <<"'\n";
    strip( s );
    cout << "   strip: '" << s <<"'\n";
    s = t;
    rstrip( s );
    cout << "  rstrip: '" << s <<"'\n";
    s = t;
    lstrip( s );
    cout << "  lstrip: '" << s <<"'\n";
   
    string t2 = "       \t    \t    \t      ";
    s = t2;
    cout << "NO trim: '" << s <<"'\n";
    trim( s );
    cout << "   trim: '" << s <<"'\n";
    s = t2;
    rtrim( s );
    cout << "  rtrim: '" << s <<"'\n";
    s = t2;
    ltrim( s );
    cout << "  ltrim: '" << s <<"'\n";
   
   
    list < string > myList;
    split( myList, t );
    cout << "\n\nmyList.size() = "<< myList.size() << endl;
   
    list < string > :: iterator it;
    int i = 0;
    for( it = myList.begin(); it != myList.end(); ++ it, ++ i)
        cout << i << ": " << *it << endl;

    cout << "\nPress 'Enter' to continue ... " << flush;
    cin.get();
}



And a C version ...


Code: [Select]
/* strip.h */

/* Note: original string passed in gets 'stripped' ... */

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


#ifndef dwSTRIP
#define dwSTRIP


#ifndef dwSTRING_H
#define dwSTRING_H
#include <string.h>
#endif
#ifndef dwCTYPE_H
#define dwCTYPE_H
#include <ctype.h>
#endif

#ifndef ltrim
#define ltrim lstrip
#endif
#ifndef rtrim
#define rtrim rstrip
#endif
#ifndef trim
#define trim strip
#endif

/* s is a local copy of pointer/address to start of C string passed on ... */
char* lstrip( char* s )
{
    if( *s == 0 )
        return s;
       
    while( isspace( *s ) ) ++s; /* while leading whitespace, advance pointer */
    return s;
}

/* yuk to above code ...now use memmove to move the right size block of char's to front */


char* rstrip( char* s )
{
    if( *s == 0 )
        return s;
    else
    {
        char* back = s + strlen(s)-1; /* back holds pointer to last char */
        while( isspace( *back ) ) --back; /*while trailing whitespace move back*/
        *(back+1) = 0; /* now set '\0' as terminal byte to C string ... */
        return s;
    }
}

char* strip( char* s ) /* return rstrip( lstrip( s ) ) ... pulled apart here */
{
    if( *s == 0 )
        return s;
    else
    {
        while( isspace( *s ) ) ++s; /* lstrip ... */
        if( *s == 0 ) return s;
        else
        {
            char* back = s + strlen( s ) - 1; /* now .... rstrip ... */
            while( isspace( *back ) ) --back;
            *(back+1) = 0;
            return s;
        }
    }
}

#endif

Addendum:  you may like to see these comments that were posted re. this above C strip/trim code ...

Quote
Comments at:

http://www.dreamincode.net/code/snippet5356.htm

1.
Don't use free() on a pointer returned by lstrip or strip or chaos might happen at runtime. If you need to use lstrip or strip on a malloc'd string, keep a copy of the value returned from malloc so you can pass it to free() later.


Reply to 1. part a ...
Good point re. dynamic C strings ... since lstrip/ltrim or strip/trim WILL change the value of the pointer to the dynamic C string if it skips over leading whitespace ... (whereas rstrip/rtrim ... is safe from this potential problem.) You may also like to see a C++ version here: http://developers-heaven.net/forum/index.php/topic,106.msg576.html#msg576


Reply to 1. part b ...
One quick solution to the above potential problem of ltrim or trim of leading whitespace in dynamic strings passed in ... would be to ... 1. save a copy of the pointer to the passed in dynamic memory C string at the top of the ltrim/trim functions ... 2. if ltrim happens ... copy the trimmed string into new dynamic memory before returning and free the old string via the saved pointer This way, the pointer returned will be valid for a later pass to 'free' the dynamic C string memory

Updated now (elsewhere on this forum ) to use memmove to move the right sized block of char's that was just left trimmed ... Forward to front positions in C string (char array)
Title: Re: New thread especially for students of C and C++
Post by: David on May 04, 2010, 10:42:22 AM
Now, continuing with C ...


Two demo programs of some C linked list basics/fun with pointers (and passing "reference pointers" in C) ...

First without using typedef (and then following, see the enhanced and simplified version that uses typedef) ...


(Without typedef)

Code: [Select]
/* list_stuff.c */

/* this version 2010-05-07 */

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

/*
    Note:
    1. for circular lists the tail points back to the head, instead of NULL
    2. for circular lists the tail is tracked/passed ... thus: head = tail->next
*/

struct Node
{
    int data;
    struct Node* next;
};

/* NON-circular list head pointer is passed in to p, a local copy */
void show( struct Node* p )
{
    if( p == NULL ) { puts( "... empty" ); return; }
    
    for( ; p != NULL; p = p->next )
        printf( "%d ", p->data );
    putchar( '\n' );
}

/* determine if the list is circular ... or NULL terminated */
int isCyclic(struct Node *head)/* Note: reports empty list as NON-cyclic ... */
{
    struct Node* start = head;
    for( ;; )
    {
        if( start==NULL || start->next==NULL ) return 0;
        else if( start->next == head ) return 1;
        else start = start->next;
    }
}

/* show list: (1) tail points to NULL ... or (2) tail points back to head */
void showCyclic( struct Node* p )
{
    struct Node* cur = p;
    if( cur && isCyclic( p ) ) p = cur = cur->next;
    int count = 0;
    if( cur != NULL )
    {
        printf( "%d ", cur->data ); /* handle first node as a special case */
        cur = cur->next;
        ++count;
        
        for( ; cur && cur != p; cur = cur->next )
            printf( "%d ", cur->data ), ++count;
        printf( "\nThe list above is %scyclic and has length %d \n",
                (cur ? "" : "NOT "), count );
    }
    else puts( "... empty" );
}

void eraseAllCyclic( struct Node** headRef ) /* handles NULL terminated also */
{
    if( *headRef != NULL )
    {
        struct Node *cur = *headRef, *start = *headRef ,*t;
        cur = start->next;
        free( start ); /* free the first node as a special case ... */
        
        for( t = cur; t &&  t!=start; t = cur ) /* handle the rest ...*/
        {
            cur = cur->next;
            free( t );
        }
        *headRef = NULL;
    }
}

#if 0 /* see above simpler version ... */
void eraseAllCyclic( struct Node** headRef )
{
    if( *headRef != NULL )
    {
        /* Note: first line is a special case for circular lists ... */
        struct Node *start = (*headRef)->next;
        if( start->next != start ) /* i.e. if NOT just one element in list */
        {
            struct Node *p, *cur = start->next;
            free( start );

            for( p = cur; p != start; p = cur )
            {
                cur = cur->next;
                free( p );
            }
        }
        else /* just one element to delete ... */
            free( start );

        *headRef = NULL;
    }
}
#endif

/*
    ============================================================
    The functions defined below pertain to NULL terminated lists
    ============================================================
    ( but note last example here ... in test program in 'main' )
*/

void eraseAll( struct Node** headRef )
{
    struct Node *cur = *headRef, *tmp;
    for( tmp = cur; cur != NULL; tmp = cur )
    {
        cur = cur->next;
        free( tmp );
    }
    *headRef = NULL;
}

void pushNode_front( struct Node** headRef, struct Node* n )
{
    n->next = *headRef;
    *headRef = n;
}

void insert_node_sorted( struct Node** r, struct Node* n )
{
    while( *r  &&  n->data > (*r)->data )
        r = &( (*r)->next ); /* move r to point to the next field of the node */
    pushNode_front( r, n );
}

void push_front( struct Node** headRef, int newData )
{
    struct Node* newNode = (struct Node*) malloc( sizeof(struct Node) );
    newNode->data = newData;
    newNode->next = *headRef;
    *headRef = newNode;
}

void insert_val_sorted( struct Node** r, const int val )
{
    while( *r  &&  val > (*r)->data )
        r = &( (*r)->next ); /* move r to point to the next field of the node */
    push_front( r, val );
}

/*
    Probably the hardest part is seeing that reverse( &rest )
    does in fact reverse the rest ... Also ... seeing the trick to
    getting the one front node all the way to the end of the list.
    Drawing a sketch might assist seeing how this works ...
*/

void reverse(struct Node** headRef)
{
    struct Node* first = *headRef;
    if( first != NULL ) /* suppose first = a, b, c... */
    {
        struct Node* rest = first->next; /* rest = b, c... */
        if( rest == NULL) return;   /* since one element only in the list */

        /* recursive call ... */
        reverse( &rest ); /* reverse the smaller  b, c... case */

        /* now unwinding stack ... */
        *headRef = rest; /* fix the head pointer */

        /* ... consider old 'first' 'a' and old 'first->next' 'b' */
        first->next->next = first; /* put the first 'a' after 'b' ... */
        first->next = NULL; /* set this 'new end' .... to be the 'end' ... */
        /*
        puts( "Unwinding stack ..." );
        show( rest );
        */
    }
}

/* reverses direction of pointers to next ... */
void rev(struct Node** headRef)
{
    struct Node *cur = *headRef, *newHead = NULL, *nxtTmp;
    while( cur != NULL )
    {
        nxtTmp = cur->next;
        cur->next = newHead;
        newHead = cur;
        cur = nxtTmp;
    }
    *headRef = newHead;
}

/* head pointer is passed in to p, a local copy */
int len( struct Node* p )
{
    int count = 0;
    for( ; p != NULL; ++ count, p = p->next ) ;
    return count;
}

/* create and return the new list 1, 2, 3, 4... */
struct Node* createNewListRange( int r ) /* r is a local copy */
{
    struct Node* head = NULL; /* start with the empty list */
    while( r ) push_front( &head, --r );
    return head;
}

struct Node* createNewListLocalRefRange( int r )
{
    struct Node* head = NULL;
    struct Node** lastPtrRef= &head; /* begins pointing to the head pointer */
    int i;
    for( i = 0; i < r; ++i )
    {
        push_front( lastPtrRef, i ); /* add at the last pointer in the list */
        lastPtrRef = &( (*lastPtrRef)->next ); /* advance to point to the
                                                  new last pointer */
    }
    return head;
}

/* isort a list in place ... head is passed in to local copy of pointer 'cur' */
struct Node* isort( struct Node* cur )
{
    struct Node *tmp, *newhead = NULL;
    while( cur != NULL )
    {
        tmp = cur;
        cur = cur->next;
        insert_node_sorted( &newhead, tmp );
    }
    return newhead;
}




int main( void )
{
    int i,
        test[] = { 1, 6, 3, 4, 5, 2, 0 }; /* some values to put into a list */
    const int size = sizeof test / sizeof test[0];
    struct Node *tail, *list = createNewListRange( 7 );
    printf( "The length of the list is %d and the list is:\n", len( list ) );
    show( list );
    
    tail = list;
    reverse( &list ); /* now tail above really holds the tail address of list */
    printf( "The length of the reverse( &list ) is %d and the list is:\n", len( list ) );
    show( list );
    
    push_front( &(tail->next), 99); /* push value here and update tail->next */
    tail = tail->next; /* update tail ... */
    push_front( &(tail->next), 101);

    printf( "After ... \n"
            "push_front( &(tail->next), 99); \n"
            "tail = tail->next; \n"
            "push_front( &(tail->next), 101); \n"
            "the length of the list is %d and the list is:\n", len( list ) );
    show( list );
    
    eraseAll( &list );
    printf( "After eraseAll( &list ) ... \n"
            "the length of the list is %d and the list is:\n", len( list ) );
    show( list );
    
    fputs( "\nPress 'Enter' to continue ... ", stdout );
    getchar();

    /* ********************************************************************** */

    list = createNewListLocalRefRange( 11 );
    printf( "The length of the new list is %d and the list is:\n", len( list ) );
    show( list );
    
    eraseAll( &list );
    for( i =0; i < size; ++ i)
        insert_val_sorted( &list, test[i] );
    printf( "The length of the (insert_val_sorted) list is %d and the list is:\n", len( list ) );
    show( list );
    
    eraseAllCyclic( &list ); /* test out on a NON-cyclic list ... */
    printf( "After eraseAllCyclic( &list ) ... "
            "length is %d and the list is:\n", len( list ) );
    show( list );
    
    for( i =0; i < 17; ++ i)
        push_front( &list, i );
    printf( "The length of the (push_front) list is %d and the list is:\n", len( list ) );
    show( list );
    
    list = isort( list );
    printf( "The length of the (isort'ed) list is %d and the list is:\n", len( list ) );
    show( list );
    
    tail = list;
    rev( &list );
    printf( "The length of the rev( &list ) is %d and the list is:\n", len( list ) );
    showCyclic( list ); /* Test out on a NON-cyclic list ... */

    tail->next = list; /* make cyclic ... */
    printf( "The next list is %scyclic ... \n", (isCyclic( tail ) ? "" : "NOT ") );
    showCyclic( tail ); /* Testing on a CYCLIC list.  Note: tail is passed in */
    
    eraseAllCyclic( &tail );
    printf( "After eraseAllCyclic( &tail ) ... the list is:\n" );
    showCyclic( tail );
    
    fputs( "\nPress 'Enter' to continue ... ", stdout );
    getchar();

    /* ********************************************************************** */

    /* since tail now is NULL ... */
    push_front( &tail, 0 ); /* ...can get first element as a special case */
    tail->next = tail; /* ok ... now ... close the loop */
    
    for( i =1; i < 17; ++ i) /* ok ... now add rest of values using a loop */
    {
        push_front( &(tail->next), i ); /* add value here and update tail->next */
        tail = tail->next;
    }
    printf( "After push_front( &(tail->next), i ); "
            "tail = tail->next; the list is:\n" );
    showCyclic( tail );

    eraseAllCyclic( &tail );
    printf( "After eraseAllCyclic( &tail ) ... the list is:\n" );
    showCyclic( tail );


    fputs( "\nPress 'Enter' to continue ... ", stdout );
    getchar();
    return 0;
}


Go to the next page [4] (click on 4 in your lower left corner) to see the enhanced version using typedef ...
Title: Re: New thread especially for students of C and C++
Post by: David on May 04, 2010, 11:01:59 AM
Now ... the enhanced and simplified version using typedef ...


Code: [Select]
/* list_stuff2.c */

/* this version 2010=05-08 */

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

/*
    Notes:
    1. for circular lists the tail points back to the head, instead of NULL
    2. for circular lists the tail is tracked/passed ... thus: head = tail->next
*/


typedef struct Node
{
    int data;
    struct Node* next;
} Node;

typedef Node* pNode;

pNode createNewNode( int val )
{
    pNode newNode = (pNode) malloc( sizeof(Node) );
    if( newNode == NULL)
    {
        fputs( "Error: malloc failed to allocate memory. "
                "Press 'Enter' to exit ... ", stderr );
        getchar();
        exit(1);
    }
    newNode->data = val;
    newNode->next = NULL;
    return newNode;
}

/* head pointer is passed in to p, a local copy */
void show( pNode p )
{
    for( ; p != NULL; p = p->next )
        printf( "%d ", p->data );
    putchar( '\n' );
}

/* returns '1' if the list is cyclic or '0' if NULL terminated ... */
int isCyclic( pNode head )
{
    pNode start = head;
    for( ;; )
    {
        if( start==NULL || start->next==NULL ) return 0;
        else if( start->next == head ) return 1;
        else start = start->next;
    }
}

/* show list: (1) tail points to NULL ... or (2) tail points back to head */
void showCyclic( pNode p )
{
    pNode cur = p;
    if( cur && isCyclic( p ) ) p = cur = cur->next;
    int count = 0;
    if( cur != NULL )
    {
        printf( "%d ", cur->data ); /* handle first node as a special case */
        cur = cur->next;
        ++count;

        for( ; cur && cur != p; cur = cur->next )
            printf( "%d ", cur->data ), ++count;
        printf( "\nThe list above is %scyclic and has length %d \n",
                (cur ? "" : "NOT "), count );
    }
    else puts( "... empty" );
}


/* reverses direction of pointers to next ... handles cyclic lists only */
void revCyclic(pNode* headRef)
{
    pNode start = *headRef;
    if( start != NULL && start != start->next ) /* if more than one element */
    {
        pNode cur, nxtTmp, newHead = NULL;
        start = start->next; /* recall: start = tail->next ... for cyclic ... */
        cur = start; /* A */

        /* ok ... switch directions of first pointer ... as special case */
        /* A->B->C  becomes  C->B->A */
        nxtTmp = cur->next;     /* address of B stored in Tmp */
        cur->next = newHead;    /* A-> N */
        newHead = cur;          /* N := A  ... A-> A */
        cur = nxtTmp;           /* A := B  ... so now ... B-> A */

        while( cur != start )   /* now can handle cur != start in a loop ... */
        {
            nxtTmp = cur->next; /* address of C stored in Tmp ... */
            cur->next = newHead;/* B-> N */
            newHead = cur;      /* N := B ... B-> B */
            cur = nxtTmp;       /* B := C ... so now ... C-> B */
        }
        /*  recall ( cur == start )  here ... */
        start->next = newHead; /* close loop: join old head (now tail) to newHead */
        *headRef = cur; /* i.e return the tail ... since a circular list */
    }
}

void eraseAllCyclic( pNode* headRef ) /* handles NULL terminated also */
{
    if( *headRef != NULL )
    {
        pNode cur = *headRef, start = *headRef, t;
        cur = start->next;
        free( start ); /* free the first node as a special case ... */

        for( t = cur; t &&  t!=start; t = cur ) /* handle the rest ...*/
        {
            cur = cur->next;
            free( t );
        }
        *headRef = NULL;
    }
}

void eraseAll( pNode* headRef )
{
    pNode n = *headRef;
    for( ; n != NULL; *headRef = n )
    {
        n = n->next;
        free( *headRef );
    }
}


void pushNodeFront( pNode* headRef, pNode n )
{
    n->next = *headRef;
    *headRef = n;
}

void insertNodeSorted( pNode* ref, pNode n )
{
    while( *ref  &&  n->data > (*ref)->data ) /* find place to insert ... */
        ref = &( (*ref)->next ); /* move ref to point to the next field of the node */
    pushNodeFront( ref, n ); /* pk ... now insert here ... */
}

void push_front( pNode* headRef, int newData )
{
    pNode newNode = createNewNode( newData );
    newNode->next = *headRef;
    *headRef = newNode;
}

void insertValSorted( pNode* ref, const int val )
{
    while( *ref  &&  val > (*ref)->data )
        ref = &( (*ref)->next ); /* move ref to point to the next field of the node */
    push_front( ref, val );
}

/*
    Probably the hardest part is seeing that reverse( &rest )
    does in fact reverse the rest ... Also ... seeing the trick to
    getting the one front node all the way to the end of the list.
    Drawing a sketch might assist seeing how this works ...
*/
void reverse(pNode* headRef) /* a recursive reverse method demo'd here ... */
{
    pNode first = *headRef;
    if( first != NULL ) /* suppose first = a, b, c... */
    {
        pNode rest = first->next; /* rest = b, c... */
        if( rest == NULL) return;   /* since one element only in the list */

        /* recursive call ... */
        reverse( &rest ); /* reverse the smaller  b, c... case */

        /* now unwinding stack ... */
        *headRef = rest; /* fix the head pointer */

        /* ... consider old 'first' 'a' and old 'first->next' 'b' */
        first->next->next = first; /* put the first 'a' after 'b' ... */
        first->next = NULL; /* set this 'new end' .... to be the 'end' ... */
        /*
        puts( "Unwinding stack ..." );
        show( rest );
        */
    }
}

/* head pointer is passed in to p, a local copy */
int len( pNode p )
{
    int count = 0;
    for( ; p != NULL; ++ count, p = p->next ) ;
    return count;
}

/* create and return the new list 1, 2, 3, 4... */
pNode createNewListRange( int r ) /* r is a local copy */
{
    pNode head = NULL; /* start with the empty list */
    while( r ) push_front( &head, --r );
    return head;
}

pNode createNewListLocalRefRange( int r )
{
    pNode head = NULL;
    pNode* lastPtrRef= &head; /* begins pointing to the head pointer */
    int i;
    for( i = 0; i < r; ++i )
    {
        push_front( lastPtrRef, i ); /* add at the last pointer in the list */
        lastPtrRef = &( (*lastPtrRef)->next ); /* advance to point to the
                                                  new last pointer */
    }
    return head;
}

pNode insertSort( pNode cur )
{
    pNode n, nhead = NULL;
    while( cur != NULL )
    {
        n = cur;
        cur = cur->next;
        insertNodeSorted( &nhead, n );
    }
    return nhead;
}



int main( void )
{
    int i, test[] = { 1, 11, 10, 9, 8, 7, 6, 3, 4, 5, 2, 0 };
    const int size = sizeof test / sizeof test[0];
    pNode tail, list = createNewListRange( 7 );
    printf( "The length of the createNewListRange( 7 ) is %d and the list is:\n",
            len( list ) );
    show( list );
    
    tail = list;
    reverse( &list );
    printf( "The length of the reverse( &list ) is %d and the list is:\n",
            len( list ) );
    show( list );
    
    push_front( &(tail->next), 99);
    tail = tail->next;
    push_front( &(tail->next), 101);

    printf( "The length AFTER THE 2ND push_front( &(tail->next), 101)"
            " is %d and the list is:\n",
            len( list ) );
    show( list );
    
    eraseAll( &list );
    printf( "After eraseAll( &list ) ... length is %d and the list is:\n",
            len( list ) );
    show( list );
    
    fputs( "Press 'Enter' to continue ... ", stdout );
    getchar();

/* ************************************************************************** */
    
    list = createNewListLocalRefRange( 11 );
    printf( "The length of the createNewListLocalRefRange( 11 )"
            " is %d and the list is:\n",
            len( list ) );
    show( list );
    
    eraseAll( &list );
    for( i =0; i < size; ++ i)
        insertValSorted( &list, test[i] );
    printf( "The length of the NEW (insertValSorted) list"
            " is %d and the list is:\n",
            len( list ) );
    show( list );
    
    eraseAllCyclic( &list ); /* test out on a NON-cyclic list ... */
    printf( "After eraseAllCyclic( &list ) ... "
            "the length is %d and the list is:\n", len( list ) );
    show( list );
    
    for( i =0; i < 17; ++ i)
        push_front( &list, i );
    printf( "The length of the NEW (push_front) list is %d and the list is:\n",
            len( list ) );
    show( list );
    
    list = insertSort( list );
    printf( "The length of the NEW (insertSort'ed) list is %d and the list is:\n",
            len( list ) );
    show( list );
    
    tail = list; /* get a copy of head pointer ... before reversing ... */
    reverse( &list );
    printf( "The length of the reversed( &list ) is %d and the list is:\n",
            len( list ) );
    showCyclic( list ); /* test out on NON-cyclic list ... */

    tail->next = list; /* make into a cyclic list ... */
    printf( "The next list is %scyclic ... \n",
            (isCyclic( tail ) ? "" : "NOT ") );
    showCyclic( tail );

    revCyclic( &tail );
    printf( "After ... revCyclic( &tail ) ... the list is %scyclic ... \n",
            (isCyclic( tail ) ? "" : "NOT ") );
    showCyclic( tail );
    eraseAllCyclic( &tail );

    printf( "After ... 'eraseAllCyclic( &tail ); showCyclic( tail );' list is " );
    showCyclic( tail );
    
    fputs( "Press 'Enter' to continue ... ", stdout );
    getchar();
    return 0;
}



And ... merging two unsorted files into one new file (in sorted order) ...
via insert_sort and one linked list in RAM ... (and using a struct to hold the data)


Code: [Select]
/* mergeTwoFiles_struct.c */ /* this version 2010-05-25 */

/*
    C demo of merging two files into a new sorted file ... using a linked-
    list ... by inserting struct, in sorted order, into the (growing) list.
    
    Note: using fscanf( fp, "%s", buffer ) to read each WORD on the file line...
    EXCEPT FOR the last chunk on the line ... That is obtained by ...
    pS->first = getString( fp ); // reads the WHOLE rest of the line ... //
    
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */

#define fName1 "info1.txt"
#define fName2 "info2.txt"
#define fName3 "merge.txt"
#define WORD_CHUNK 32 /* adjust to your data's size to minimize realloc calls */

/*
info1.txt

09876 year1 Smith Catee Rose
08765 year2 Clinton Hilary Claire
07654 year3 Barrack Richard Kenneth

info2.txt

08967 year1 Edison Bill
06789 year2 Bell Robert Ed

merge.txt

07654 year3 Barrack Richard Kenneth
06789 year2 Bell Robert Ed
08765 year2 Clinton Hilary Claire
08967 year1 Edison Bell
09876 year1 Smith Catee Rose
*/

typedef struct myStudent
{
    char* id;
    char* year;
    char* last;
    char* first;
    struct myStudent* next;
} Student;

typedef Student* pStudent;

/* these 2 global var's used to simplify function calls ... */
pStudent pHead = NULL; /* Note: need a NULL value to start inserts ... */
int numStudents = 0;

void insert( pStudent pS );
int readFile( char* name );
int writeFile( char* name );
int studentCmp( pStudent pS1, pStudent pS2 );
char* newCopy( char* str );
char* getString( FILE* fp );
void view( pStudent pS );
void showAll();
void delAll();
void flushStdin();
void myAssert( int condition, char message[] );


int main() /* ********************* start of main *************************** */
{
    int num = readFile( fName1 );
    if( num )
        numStudents += num;

    num = readFile( fName2 );
    if( num )
    numStudents += num;

    //showAll();

    num = writeFile( fName3 );
    if( num == numStudents )
        printf
        (
            "All %d students were successfully sorted and merged in file %s.",
            num, fName3
        );
    else
        printf
        (
            "Error: writing to file %s  Note: wrote %d vs. numStudents of %d",
            fName3, num, numStudents
        );

    delAll();
    printf("\n\nPress 'Enter' to continue ... ");
    flushStdin();

    /* if using Windows OS ... */
    system( "notepad merge.txt" );
    return 0;
    
} /* ******************************* end of main **************************** */



void flushStdin()
{
    while( getchar()!='\n' ) ;
}

char* newCopy( char* str )
{
    char* nCopy = (char*) malloc( strlen(str) + 1 );
    myAssert( (nCopy != NULL), "Error: malloc failed (0) " );
    strcpy( nCopy, str );
    return nCopy;
}

char* getString( FILE* fp )
{
    int c, i= 0, chunk = WORD_CHUNK;
    char* tmp;
    char* buffer = (char*) calloc( chunk, 1 );
    myAssert( (buffer != NULL), "Error: calloc failed (1) " );
    while( (c=fgetc(fp))!=EOF && c!='\n' ) /*eats up WHOLE line including '\n'*/
    {
        if( i == chunk - 2 )
        {
            chunk += WORD_CHUNK;
            tmp = (char*) realloc( buffer, chunk );
            if( tmp == NULL )
            {
                free( buffer );
                myAssert( 0, "Error: realloc failed (2) " );
            }
            /* else ...  update buffer address ... */
            buffer = tmp;
        }
        buffer[i++] = c;
    }
    buffer[i] = 0;
    tmp = (char*) realloc( buffer, i+1  );
    if( tmp == NULL )
    {
        free( buffer );
        myAssert( 0, "Error: realloc failed (3) " );
    }
    return tmp;
}

/* insert in list with last & first names in proper order */
void insert( pStudent pS )
{
    pStudent q = pHead; /* Get a working copy of pHead in q */
    
    /* Firstly, we handle the case where 'this' should be the first element. */
    if(pHead == NULL || studentCmp(pS, pHead) < 0)
    {
        /* So ... it now becomes the first element ... */
        pS->next = pHead; /* old pHead becomes 2nd in list ... */
        pHead = pS; /* and ... this pS ... becomes the head of the list */
    }
    else /* If here ... search the linked list for the right location ... */
    {
        /* Start comparing with element AFTER 'q' ... i.e. the 'next in' ... */
        while(q->next != NULL && studentCmp(q->next, pS) <= 0)
        {
            q = q->next; /* Traverse the list ... */
        }
        /*
            Ok, insert after 'q' by relinking the pointers (similar to above)
            ( Includes inserting at end position ... where q->next == NULL )
        */
        pS->next = q->next; /* Inserted 'pS' is linked to 'upstream element' */
        q->next = pS;  /* Now 'q' is updated to link to the new 'pS' element */
    }
}

void view( pStudent pS )
{
    printf
    (
        "ID: %s  "
        "Year: %s  "
        "Name: %s,%s", /* Note: 2nd string already contains a leading space */
        pS->id, pS->year, pS->last, pS->first
    );
}

void showAll()
{
    pStudent p = pHead;     /* int c; */
    if( pHead == NULL )
    {
        puts("\nNo records in memory at present.") ;
        return;
    }

    /* If reach here ... */
    while( p != NULL )
    {
        view( p );
        putchar( '\n' );
        p = p->next;
    }
    fflush( stdout );
}

int writeFile(char *name)
{
    FILE* fp;
    int count = 0; /* to track the records actually written to the file */
    pStudent p = pHead;
    if( p == NULL )
    {
        puts("\nNo records available ... so NO records written to file.") ;
        return 0;
    }
    
    fp = fopen( name, "w" );
    if( fp == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", name);
        flushStdin();
        return 0;
    }

    while( p != NULL )
    {
        fprintf
        (
            fp,
            "%s "
            "%s "
            "%s" /*no space here ... since a leading space is in next string*/
            "%s\n",
            p->id, p->year, p->last, p->first
        );

        ++count;
        p = p->next;
    }
    fclose( fp );
    return count; /* Number of records written. */
}

int readFile( char* name )
{
    int count = 0;
    pStudent pS;
    char buffer[WORD_CHUNK+1];
    FILE* fp = fopen( name, "r" );
    if( fp  == NULL )
    {
        printf
        (
            "\nError opening file %s.\n"
            "Perhaps it hasn't been created yet?\n"
            "Press 'Enter' to continue ... ",
            name
        );
        flushStdin();
        return 0;
    }

    /* If the program reaches here ... */
    while(  fscanf(fp, "%s", buffer) != EOF  )
    {
        pS = (pStudent) malloc( sizeof(Student) );
        myAssert( (pS != NULL), "Error: malloc failed (4) " );
        
        pS->id = newCopy( buffer );

        fscanf( fp, "%s", buffer );
        pS->year = newCopy( buffer );

        fscanf( fp, "%s", buffer );
        pS->last = newCopy( buffer );

        pS->first = getString( fp ); /* reads the WHOLE rest of the line ... */

        insert( pS );
        ++count;
    }
    fclose( fp );

    printf("%d records were read into memory from the file %s.\n", count, name);

    return count; /* Total number of student records found in the file. */
}

/* A function to compare two student records to permit sorting ... */
int studentCmp( pStudent pS1, pStudent pS2 )
{
    int compare = strcmp(pS1->last, pS2->last); /* compare last_names */
    if ( compare == 0 ) /* if last_names same ... use first_names */
        return strcmp(pS1->first, pS2->first);
    return compare; /* use last_names after all since different */
}

void delAll()
{
    pStudent p = pHead; /* set p to this initial value to start loop */
    pStudent pNext;     /* to hold the pointer to the next record */

    while( p != NULL )
    {
        pNext = p->next; /* get pointer to the next record */

        free( p->first );
        free( p->last );
        free( p->year );
        free( p->id );
        
        free( p );

        p = pNext; /* update p ... */
    }

    /* update globals ...  */
    pHead = NULL;
    numStudents = 0;
}

void myAssert( int condition, char message[] )
{
    if( !condition )
    {
        fputs( message, stderr );
        getchar();
        exit(1);
    }
}


Here are some more merge/sorts of 2 files (that wouldn't fit here) ...

and a way to sort a file too big for memory ...
(by breaking it up into 2 parts and sorting each part, then merging the 2 sorted files)

http://developers-heaven.net/forum/index.php/topic,106.msg611.html#msg611
Title: Re: New thread especially for students of C and C++
Post by: David on May 07, 2010, 06:57:07 AM
Check back soon ... for a mail merge demo in C, using a list of a list of lines of C strings, for a 'keyed' dictionary ...

(Also ... featuring a circular linked list to speed up push_back ... and searches for data that is mostly ordered.)

(Actually ... please contact me, if you are interested in this useful program for professional contractors ... who manage many job sites in a single project ... each job site tasks, pulled from a master-dictionary-of-keyed-tasks to be done there.)
Title: Re: New thread especially for students of C and C++
Post by: David on May 12, 2010, 07:10:20 AM
Here is a step on the way there ... to a mail merge program ...  

Take a look at this split_demo.c that shows how to split a line ( a CString ) ...
and to return a Clist of words/tokens ...

Note: You will need all three files that follow, in the same folder, to compile/run this split_demo.c ...
(First the demo file ... then the three helper files ...)

Code: [Select]
/* split_demo.c */ /* this version 2010-07-13 */

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

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

#define DELIMITS "* .?!,;:\t-" /* if don't define ... becomes "\t " */

#include "split.h"  /*
                        includes ClistOfString.h ... that includes ...
                        stdio.h, stdlib.h, string.h ... and adds ...
                        readLine, myAssert
                    */

int main() /* ********************** BEGIN MAIN ***************************** */
{
    char s[] ="*** See! *** Sample words, split-up?  ";
    char* s2 = "This, ... sample string, into tokens ... ";
    Clist myCList;
    List ml;
    init( &myCList ); /* Note: MUST initial list for it to work properly ... */

    printf( "For delimits '%s', \n", DELIMITS );
    printf( "splitting string '%s' \n", s );
    split( &myCList, s );
    showAll( &myCList );

    msort( &myCList );
    puts( "\nAfter msort ... " );
    showAll( &myCList );
    printf( "... and after msort ... the original string is still: \n"
          "'%s'\n", s );

    fputs( "\nPress 'Enter' to continue ... ", stdout );
    getchar();

    split( &myCList, s );

    ml.line = newCopy( "At_the_end_at_first ..." );
    push_back( &myCList, &ml );

    ml.line = newCopy( "z_But_at_the_beginning_at_first ..." );
    push_front( &myCList, &ml );

    msort( &myCList );
    puts( "\nAfter ... (another) split( &myCList, s ); (then) msort( &myCList ); ... " );
    showAll( &myCList );
    printf( "Note ... the original string is now: \n"
          "'%s'\n", s );

    fputs( "Press 'Enter' to continue ... ", stdout );
    getchar();
   
    freeAll( &myCList );
    showAll( &myCList );
   
    split( &myCList, s );
    split( &myCList, s2 );
    split( &myCList, s );
    puts( "\nAfter ... split( &myCList, s ); split( &myCList, s2 ); "
          "split( &myCList, s ); ... " );
    showAll( &myCList );
    printf( "Note ... the 1st original string is now: \n"
          "'%s'\n", s );
    printf( "Note ... the 2nd original string is now: \n"
          "'%s'\n", s2 );

    fputs( "Press 'Enter' to continue ... ", stdout );
    getchar();

    msort( &myCList );
    puts( "\nAfter msort ... " );
    showAll( &myCList );
    printf( "Note ... the 1st original string is now: \n"
          "'%s'\n", s );
    printf( "Note ... the 2nd original string is now: \n"
          "'%s'\n", s2 );

    freeAll( &myCList );
    showAll( &myCList );
    fputs( "Press 'Enter' to continue ... ", stdout );
    getchar();

    return 0;
} /* ******************************** END MAIN ****************************** */



And a slightly different form of split, that preserves any empty field, if delimiters are adjacent ...
(Note: this 2nd demo uses split2.h)

Code: [Select]
/* split_demo_2.c */ /* this version 2010-07-13 */

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

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

#define DELIMITS ", -" /* if don't define ... becomes "\t " */

#include "split2.h"  /*
                        includes ClistOfString.h ... that includes ...
                        stdio.h, stdlib.h, string.h ... and adds ...
                        readLine, myAssert
                    */

#define TESTFILE "test.txt" /* 9 lines (with 3 blank) */
/*

2, ,,Hi-there,,my,dearest
3, Mary

5, What,can,I,do,for,you?,

7, Dearest,
8, You,can,go,,,to,the,store,for,me.,,,
9, my dear

*/


int main() /* ********************** BEGIN MAIN ***************************** */
{
    FILE* fp = fopen( TESTFILE, "r" );
    char* s; /* (pointer to) C string ... to hold a line of 'words' ... */
    Clist myCL;
    init( &myCL ); /* Note: MUST initial list for it to work properly ... */

    myAssert( (fp != NULL), "Error: file " TESTFILE " failed to open." );
    while( (s = readLine(fp)) )
    {
        printf( "For delimits ... '%s', ", DELIMITS );
        printf( "splitting string ... \n'%s' \n", s );
        split( &myCL, s );
       
        //showAll( &myCL );
        {
            int i = 0;
            pList p = myCL.start;
            for( ; p != NULL; p = p->next )
                printf( "<%02d> '%s'\n", ++i, p->line );
            printf( "myCL.size = %d\n", myCL.size );
            putchar('\n');
        }

        free( s );
        freeAll( &myCL );
    }
    fclose( fp );
   
    /* Note: used a new block here ... just to add this code here ... */
   
    {   /* Note: '\0' char at index 7 AND at index 9 ... so split stops at 7 */
        char ary[] = { '0', ';', '2', '3', ' ', '5', ';', '\0', '8', '\0' };
        int i = 0, len = sizeof ary;
        printf( "ary len = %d, ary = '", len );
        for( ; i < len; ++i ) putchar( ary[i] );
        puts("'");
        split( &myCL, ary );
        showAll( &myCL );
        freeAll( &myCL );
    }

    printf( "Press 'Enter' to exit ... " ); fflush( stdin );
    getchar();
    return 0;
} /* ******************************** END MAIN ****************************** */
Title: Re: New thread especially for students of C and C++
Post by: David on May 12, 2010, 07:15:00 AM
Here are the three helper files needed for the above program(s) to compile/run ...


Code: [Select]
/* split.h */ /* this version: 2010-07-13 */

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

#ifndef dwSPLIT_H
#define dwSPLIT_H

#ifndef DELIMITS
#define DELIMITS  " \t"
#endif

#define NUM_DLMTS  sizeof(DELIMITS) -1

#include "ClistOfString.h" /* adds stdio, stdlib, string, readLine, myAssert */


char* createNewString( int len )
{
char* n;
    if( len < 0 ) len = 0;
    n = (char*) malloc(len+1);
    myAssert( (n!=NULL), "Error: malloc failed to allocate memory." );
    n[0] = 0;
    return n;
}

char* newCopy( const char* s )
{
    int slen = strlen(s);
    char* ncopy = createNewString(slen);
    strcpy(ncopy, s);
    return ncopy;
}

char* substr( const char* start, const char* end )
{
    int len = end-start+1;
    char* newCpy = createNewString(len);
    strncpy( newCpy, start, len );
    newCpy[len] = 0;
    return newCpy;
}

/* returns POSITION 1..len if in string ... otherwise, returns 0 */

int chrInString( const char* s, char c )
{
    int i;
    for( i =0; s[i] != 0; ++i )
        if( c ==  s[i] )
            return i+1;
    return 0;
}

void split( Clist* lst, char* s )
{
    char *p1 = s, *p2;
    List ml;
    for( ; ; ) /* loop forever ... until break */
    {
        while( *p1 != 0 && strchr(DELIMITS, *p1) ) ++p1;
        if( *p1 == 0 )
break; /* i.e. if empty or all delimits */

        p2 = p1+1;
        while( *p2 != 0 && !strchr(DELIMITS, *p2) ) ++p2;
        ml.line = substr( p1, p2-1 ); /* new copy in new memory in ml.line */
        push_back( lst, &ml );
        p1 = p2;
    }
}


#endif

Also see ...
http://developers-heaven.net/forum/index.php/topic,2584.0.html

And the 2nd version, for the 2nd demo above ...

Code: [Select]
/* split2.h */ /* this version: 2010-07-13 */

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

#ifndef dwSPLIT2_H
#define dwSPLIT2_H

#ifndef DELIMITS
#define DELIMITS  " \t"
#endif

#define NUM_DLMTS  sizeof(DELIMITS) -1

#include "ClistOfString.h" /* adds stdio, stdlib, string, readLine, myAssert */


char* createNewString( int len )
{
char* n;
    if( len < 0 ) len = 0;
    n = (char*) malloc(len+1);
    myAssert( (n!=NULL), "Error: malloc failed to allocate memory." );
    n[0] = 0;
    return n;
}

char* newCopy( const char* s )
{
    int slen = strlen(s);
    char* ncopy = createNewString(slen);
    strcpy(ncopy, s);
    return ncopy;
}

char* substrLen( const char* start, int len )
{
    char* newCpy = createNewString(len);
    strncpy( newCpy, start, len );
    newCpy[len] = 0;
    return newCpy;
}

/* returns POSITION 1..len if in string ... otherwise, returns 0 */

int chrInString( const char* s, char c )
{
    int i;
    for( i =0; s[i] != 0; ++i )
        if( c ==  s[i] )
            return i+1;
    return 0;
}

void split( Clist* lst, char* s )
{
    List ml;
    char* t = s + strlen(s); /* t is address of one past last char in line */
    while( s < t ) /* loop while s is still IN the line ... */
    {
        /* search for delimiters ... including ALSO '\0' at end of line ... */
        size_t len = strcspn(s, DELIMITS);
        ml.line = substrLen( s, len ); /* copy is in new memory in ml.line */
        push_back( lst, &ml );
        /* advance pointer to just beyond 'delimiter' at end of this 'word' */
        s += len+1;
    }
}

#endif


Code: [Select]
/* ClistOfString.h */ /* this version: 2010-07-13 */

#ifndef dwClistOfString_H
#define dwClistOfString_H

#ifndef sort_offset
#define sort_offset 0
#endif

/* using readLine here ... instead of gets and fgets */
#include "readLine.h" /* includes stdio.h, stdlib.h  ... also myAssert( ... ) */

/* Note: stdio.h, stdlib.h and myAssert were included in "readLine.h" above */
#ifndef dwSTRING_H
#define dwSTRING_H
#include <string.h> /* re. memcpy */
#endif

typedef struct ClistOfString
{
    char* line;     /* since CStrings are '\0' terminated ... can get strlen */
    struct ClistOfString* next;
} List ;

typedef List* pList;

typedef struct myClist
{
    pList start;
    pList end;
    int size;
    int isSorted;
} Clist;

/* with these, an address is passed, so NO copy made and/or original updated */
void init( Clist* ); /* sets start to NULL, size to 0. isSorted to 1 */
void push_front( Clist*, List* );
void push_back( Clist*, List* );
void freeAll( Clist* );
void show( pList );
void showAll( Clist* );

void msort( Clist* );
void mergesort( Clist* );
pList merge( Clist*, Clist* );
void update_end( Clist* );


void init( Clist* list )
{
    list->start = list->end = NULL;
    list->size = 0;
    list->isSorted = 1;
}

void push_front( Clist* list, List* d )
{
    pList p = (pList) malloc( sizeof(List) );
    if( p == NULL )
    {
        freeAll( list );
        myAssert( 0, "Error: malloc failed to allocate memory." );
    }

    /* now add in ALL new dat ... (assuming next pointer is last of dat) */
    memcpy( p, d, sizeof(List)-sizeof(pList) ); /* -sizeof(any_pointer) is ok */
    /* and set pointers to next ... and start ...*/
    p->next = list->start;
    list->start = p;

    ++ list->size;
    if( list->size > 1 ) list->isSorted = 0;
    else list->end = list->start;
}

void push_back( Clist* list, List* d )
{
    pList p = (pList) malloc( sizeof(List) );
    if( p == NULL )
    {
        freeAll( list );
        myAssert( 0, "Error: malloc failed to allocate memory." );
    }

    /* now add in ALL new dat ... (assuming next pointer is last of dat) */
    memcpy( p, d, sizeof(List)-sizeof(pList) ); /* -sizeof(any_pointer) is ok */
    /* and set pointers to next ... and start ...*/

    p->next = NULL;
    ++ list->size;
    if( list->size > 1 )
    {
        list->end->next = p;
        list->end = p;
        list->isSorted = 0;
    }
    else
        list->start = list->end = p;
}

void freeAll( Clist* list )
{
    //printf( "\nFreeing list->size  of %d ... \n", list->size );
    if( list->size > 0 )
    {
        pList cur = list->start;
        for( ; cur != NULL; cur = list->start  )
        {
            list->start = cur->next;
            free( cur->line );
            free( cur );
        }
        init( list );
    }
}

void show( pList pd )
{
    printf( "%s\n", pd->line );
}
void showAll( Clist* list )
{
    if( list->size )
    {
pList p = list->start;
for( ; p != NULL; p = p->next )
show( p );
printf( "List size = %d\n", list->size );
    }
    else puts( "The list is empty ... " );
}


/* a recursive mergesort ... */
void mergesort(Clist* list)
{
    pList cur = list->start;
    Clist a, b;

    /* base case is a Clist of length 0 or 1 ... */
    if ((cur == NULL) || (cur->next == NULL))  return;

    /* split Clist into 'a' and 'b' sublists ... */
    a.start = cur;
    b.start = cur->next;
    while((b.start != NULL) && (b.start->next != NULL))
    {
        cur = cur->next;
        b.start = b.start->next->next;
    }
    b.start = cur->next;
    cur->next = NULL; /* Clist divided into 2 roughly equal parts now ... */

    /* recursively sort the sublists ... */
    mergesort(&a);
    mergesort(&b);

    /* merge the two sorted Clists together ... */
    list->start = merge(&a, &b);
    list->isSorted = 0;
}


/* merge two sorted Clists with heads 'a' and 'b' ... in sorted order */
pList merge(Clist* a, Clist* b )
{
    pList sorted, new_merged_head;

    if( a->start == NULL ) return b->start;
    if( b->start == NULL ) return a->start;

    if( strcmp(a->start->line + sort_offset, b->start->line + sort_offset) <= 0 )
    {
        sorted = a->start;
        a->start = a->start->next;
    }
    else
    {
        sorted = b->start;
        b->start = b->start->next;
    }
    new_merged_head = sorted;

    /* now ... */
    while( a->start != NULL && b->start != NULL )
    {
        if( strcmp(a->start->line + sort_offset, b->start->line + sort_offset) <= 0 )
        {
            sorted->next = a->start;
            sorted = a->start;
            a->start = a->start->next;
        }
        else
        {
            sorted->next = b->start;
            sorted = b->start;
            b->start = b->start->next;
        }
    }

    /* and finally ... */
    if( a->start != NULL )
        sorted->next = a->start;
    else if( b->start != NULL )
        sorted->next = b->start;

    return new_merged_head;
}

void update_end( Clist* list ) /* after sort */
{
    if( list->size > 1 )
    {
        pList cur;
        for( cur = list->start; cur->next != NULL; cur = cur->next ) ;
        list->end = cur;
        list->end->next = NULL;
    }
}

void msort( Clist* clst )
{
    mergesort( clst );
    update_end( clst );
}

#endif /* end of ifndef dwSTRING_H ... */


And you can find readLine.h here ...

http://developers-heaven.net/forum/index.php/topic,106.msg564.html#msg564


For an other version of split.h, ClistOfString.h with Clist.h separated out ... Cvec ... and demo programs that use these ... see ...

http://developers-heaven.net/forum/index.php/topic,466.0.html
Title: Re: New thread especially for students of C and C++
Post by: David on May 26, 2010, 10:06:31 PM
Merging ... using a linked-list, insertsort and fgets to read each line (no struct) ...
(and to sort lines using an offset into that line)
(placed here, since wouldn't fit above, with other files merging/sorting examples.)


Code: [Select]
/* mergeTwoFiles_fgets.c */ /* this version 2010-05-25 */

/*
    C demo of merging two files into a new sorted file ... using a linked-
    list ... by inserting lines, in sorted order, into the (growing) list.
    
    Note: using while( fgets(buffer, MAXLEN+1, fp) ) to read each file line,
    so ... max line length is limited to the define value of MAXLEN
    
    Pick MAXLEN so '\n' char is always included at the end of each line read in
    
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */

#define fName1 "info1.txt"
#define fName2 "info2.txt"
#define fName3 "merge.txt"
#define MAXLEN 132

#define sort_offset 12 /* so files will be sorted by names ... */

/*
info1.txt

09876 year1 Smith Catee Rose
08765 year2 Clinton Hilary Claire
07654 year3 Barrack Richard Kenneth

info2.txt

08967 year1 Edison Bill
06789 year2 Bell Robert Ed

merge.txt

07654 year3 Barrack Richard Kenneth
06789 year2 Bell Robert Ed
08765 year2 Clinton Hilary Claire
08967 year1 Edison Bell
09876 year1 Smith Catee Rose
*/

typedef struct myStudent
{
    char* dataLine;
    struct myStudent* next;
} Student ;

typedef Student *pStudent;

/* using these two global variable to ease function calls ... */
pStudent pHead = NULL; /* Note: needs a NULL value to start inserts ... */
int numStudents = 0;

void insert( pStudent pS );
int readFile( char* name );
int writeFile( char* name );
int studentCmp( pStudent pS1, pStudent pS2 );
char* newCopy( char* str );
void view( pStudent pS );
void showAll();
void delAll();
void flushStdin();
void myAssert( int condition, char nessage[] );



int main() /* ********************* start of main *************************** */
{
    int num = readFile( fName1 );
    if( num )
        numStudents += num;

    num = readFile( fName2 );
    if( num )
        numStudents += num;

    //showAll();

    num = writeFile( fName3 );
    
    if( num == numStudents )
        printf
        (
            "All %d students were successfully sorted and merged in file %s.",
            num, fName3
        );
    else
        printf
        (
            "Error: writing to file %s  Note: wrote %d vs. numStudents of %d",
            fName3, num, numStudents
        );

    delAll();
    printf("\n\nPress 'Enter' to continue ... ");
    flushStdin();

    system( "notepad merge.txt" );
    return 0;
    
} /* ******************************* end of main **************************** */



void flushStdin()
{
    while( getchar()!='\n' ) ;
}

char* newCopy( char* str )
{
    char* nCopy = (char*) malloc( strlen(str) + 1 );
    myAssert( (nCopy != NULL), "Error: malloc failed (1)" );
    strcpy( nCopy, str );
    return nCopy;
}

/* insert in list with last & first names in proper order */
void insert( pStudent pS )
{
    pStudent q = pHead; /* Get a working copy of pHead in q */
    
    /* Firstly, we handle the case where 'this' should be the first element. */
    if(pHead == NULL || studentCmp(pS, pHead) < 0)
    {
        /* So ... it now becomes the first element ... */
        pS->next = pHead; /* old pHead becomes 2nd in list ... */
        pHead = pS; /* and ... this pS ... becomes the head of the list */
    }
    else /* If here ... search the linked list for the right location ... */
    {
        /* Start comparing with element AFTER 'q' ... i.e. the 'next in' ... */
        while(q->next != NULL && studentCmp(q->next, pS) <= 0)
        {
            q = q->next; /* Traverse the list ... */
        }
        /*
            Ok, insert after 'q' by relinking the pointers (similar to above)
            ( Includes inserting at end position ... where q->next == NULL )
        */
        pS->next = q->next; /* Inserted 'pS' is linked to 'upstream element' */
        q->next = pS;  /* Now 'q' is updated to link to the new 'pS' element */
    }

}

void view( pStudent pS )
{
    printf( pS->dataLine ); /* string already has '\n' at end ... */
    fflush( stdout );
}

void showAll()
{
    pStudent p = pHead;     /* int c; */
    if( pHead == NULL )
    {
        puts("\nNo records in memory at present.") ;
        return;
    }

    /* If reach here ... */
    while( p != NULL )
    {
        view( p );
        p = p->next;
    }
}

int writeFile(char *name)
{
    FILE* fp;
    int count = 0; /* to track the records actually written to the file */
    pStudent p = pHead;
    if( p == NULL )
    {
        puts("\nNo records available ... so NO records written to file.") ;
        return 0;
    }
    
    fp = fopen( name, "w" );
    if( fp == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", name);
        flushStdin();
        return 0;
    }

    for( ; p != NULL; p = p->next )
    {
        fprintf( fp, "%s", p->dataLine ); /* Note: 'line' already has '\n' at end */
        ++count;
    }
    fclose( fp );
    return count; /* Number of records written. */
}

int readFile( char* name )
{
    int count = 0;
    pStudent pS;
    char buffer[MAXLEN+1];
    FILE* fp = fopen( name, "r" );
    if( fp  == NULL )
    {
        printf
        (
            "\nError opening file %s.\n"
            "Perhaps it hasn't been created yet?\n"
            "Press 'Enter' to continue ... ",
            name
        );
        flushStdin();
        return 0;
    }

    /* If the program reaches here ... */
    while(  fgets( buffer, MAXLEN+1, fp )  )
    {
        pS = (pStudent) malloc( sizeof(Student) );
        myAssert( (pS != NULL), "Error: malloc failed (2)" );
        pS->dataLine = newCopy( buffer );
        insert( pS );
        ++count;
    }
    fclose( fp );
    printf("%d records were read into memory from the file %s.\n", count, name);
    return count; /* Total number of student records found in the file. */
}

/* A function to compare two student records to permit sorting ... */
int studentCmp( pStudent pS1, pStudent pS2 )
{
    return strcmp( pS1->dataLine+sort_offset, pS2->dataLine+sort_offset );
}

void delAll()
{
    pStudent p = pHead; /* set p to this initial value to start loop */
    pStudent pNext;     /* to hold the pointer to the next record */
    for( ; p != NULL; p = pNext  )
    {
        pNext = p->next; /* get pointer to the next record */
        free( p->dataLine );
        free( p );
    }
    /* update globals ...  */
    pHead = NULL;
    numStudents = 0;
}

void myAssert( int condition, char message[] )
{
    if( !condition )
    {
        fputs( message, stderr );
        getchar();
        exit(1);
    }
}



As above, but using readLine instead of fgets ... so ... NO max line length (except as set by available memory)

Code: [Select]
/* mergeTwoFiles_readLine.c */ /* this version 2010-05-25 */

/*
    C demo of merging two files into a new sorted file ... using a linked-
    list ... by inserting lines, in sorted order, into the (growing) list.
    
    Note: using while( (line = readLine(fp)) ) to read each file line,
    so ... NO max line length ... (except as limited by available memory)
    
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

#include "readLine.h" /* includes stdio.h, stdlib.h and myAssert */

#include <string.h> /* re. strcmp */

#define fName1 "info1.txt"
#define fName2 "info2.txt"
#define fName3 "merge.txt"

#define sort_offset 12 /* so files will be sorted by names ... */

/*
info1.txt

09876 year1 Smith Catee Rose
08765 year2 Clinton Hilary Claire
07654 year3 Barrack Richard Kenneth

info2.txt

08967 year1 Edison Bill
06789 year2 Bell Robert Ed

merge.txt

07654 year3 Barrack Richard Kenneth
06789 year2 Bell Robert Ed
08765 year2 Clinton Hilary Claire
08967 year1 Edison Bell
09876 year1 Smith Catee Rose
*/

typedef struct myStudent
{
    char* dataLine;
    struct myStudent* next;
} Student ;

typedef Student *pStudent;

/* using these two global variable to ease function calls ... */
pStudent pHead = NULL; /* Note: needs a NULL value to start inserts ... */
int numStudents = 0;

void insert( pStudent pS );
int readFile( char* fName );
int writeFile( char* fName );
int studentCmp( pStudent pS1, pStudent pS2 );
void view( pStudent pS );
void showAll();
void delAll();
void flushStdin();


int main() /* ********************* start of main *************************** */
{
    int num = readFile( fName1 );
    if( num )
        numStudents += num;

    num = readFile( fName2 );
    if( num )
        numStudents += num;

    //showAll();

    num = writeFile( fName3 );
    
    if( num == numStudents )
        printf
        (
            "All %d students were successfully sorted and merged in file %s.",
            num, fName3
        );
    else
        printf
        (
            "Error: writing to file %s  Note: wrote %d vs. numStudents of %d",
            fName3, num, numStudents
        );

    delAll();
    printf("\n\nPress 'Enter' to continue ... ");
    flushStdin();

    system( "notepad merge.txt" );
    return 0;
    
} /* ******************************* end of main **************************** */



void flushStdin()
{
    while( getchar()!='\n' ) ;
}


/* insert in list with last & first names in proper order */
void insert( pStudent pS )
{
    pStudent q = pHead; /* Get a working copy of pHead in q */
    
    /* Firstly, we handle the case where 'this' should be the first element. */
    if(pHead == NULL || studentCmp(pS, pHead) < 0)
    {
        /* So ... it now becomes the first element ... */
        pS->next = pHead; /* old pHead becomes 2nd in list ... */
        pHead = pS; /* and ... this pS ... becomes the head of the list */
    }
    else /* If here ... search the linked list for the right location ... */
    {
        /* Start comparing with element AFTER 'q' ... i.e. the 'next in' ... */
        while(q->next != NULL && studentCmp(q->next, pS) <= 0)
        {
            q = q->next; /* Traverse the list ... */
        }
        /*
            Ok, insert after 'q' by relinking the pointers (similar to above)
            ( Includes inserting at end position ... where q->next == NULL )
        */
        pS->next = q->next; /* Inserted 'pS' is linked to 'upstream element' */
        q->next = pS;  /* Now 'q' is updated to link to the new 'pS' element */
    }

}

void view( pStudent pS )
{
    puts( pS->dataLine );
    fflush( stdout );
}

void showAll()
{
    pStudent p = pHead;     /* int c; */
    if( pHead == NULL )
    {
        puts("\nNo records in memory at present.") ;
        return;
    }

    /* If reach here ... */
    while( p != NULL )
    {
        view( p );
        p = p->next;
    }
}

int writeFile(char *fName)
{
    FILE* fp;
    int count = 0; /* to track the records actually written to the file */
    pStudent p = pHead;
    if( p == NULL )
    {
        puts("\nNo records available ... so NO records written to file.") ;
        return 0;
    }
    
    fp = fopen( fName, "w" );
    if( fp == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", fName);
        flushStdin();
        return 0;
    }

    for( ; p != NULL; p = p->next )
    {
        fprintf( fp, "%s\n", p->dataLine );
        ++count;
    }
    fclose( fp );
    return count; /* Number of records written. */
}

int readFile( char* fName )
{
    int count = 0;
    pStudent pS;
    char* line;
    FILE* fp = fopen( fName, "r" );
    if( fp  == NULL )
    {
        printf
        (
            "\nError opening file %s.\n"
            "Perhaps it hasn't been created yet?\n"
            "Press 'Enter' to continue ... ",
            fName
        );
        flushStdin();
        return 0;
    }

    /* If the program reaches here ... */
    while(  (line = readLine(fp)) )
    {
        pS = (pStudent) malloc( sizeof(Student) );
        myAssert( (pS != NULL), "Error: malloc failed (2)" );
        pS->dataLine = line;
        insert( pS );
        ++count;
    }
    fclose( fp );
    printf("%d records were read into memory from the file %s.\n", count, fName);
    return count; /* Total number of student records found in the file. */
}

/* A function to compare two student records to permit sorting ... */
int studentCmp( pStudent pS1, pStudent pS2 )
{
    return strcmp( pS1->dataLine+sort_offset, pS2->dataLine+sort_offset );
}

void delAll()
{
    pStudent p = pHead; /* set p to this initial value to start loop */
    pStudent pNext;     /* to hold the pointer to the next record */
    for( ; p != NULL; p = pNext  )
    {
        pNext = p->next; /* get pointer to the next record */
        free( p->dataLine );
        free( p );
    }
    /* update globals ...  */
    pHead = NULL;
    numStudents = 0;
}



And here is a way to sort a file too large for memory ....
break it into 2 parts and sort each file using a merge sort and then merge the 2 sorted files

Code: [Select]
/* mergeTwoFiles_msortAndMerge.c */ /* this version 2010-05-25 */

/*
    C demo of merging two files into a new sorted file ...
    1st, read each file into a linked-list,
    2nd, then msort and write file back sorted to a new sorted file
    3rd, then merge the 2 sorted files, line by line, into a merge.txt file
    
    Note 1: using my ClistOfString.h and my readLine.h ... included below
    
    Note 2: using while( (line = readLine(fp)) ) to read each file line,
    so ... NO max line length ... (except as limited by available memory)
    
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

/* set sort_offset to 12 here, so file lines will be sorted by name ... */
#define sort_offset 12 /* if sort_offset not defined here, default is 0 */

#include "ClistOfString.h" /* includes <string.h>, "readLine.h" that includes */
                           /* stdio.h, stdlib.h and myAssert */

#define fName1 "info1.txt"
#define fName2 "info2.txt"
#define fName3 "merge.txt"

/*
info1.txt

09876 year1 Smith Catee Rose
08765 year2 Clinton Hilary Claire
07654 year3 Barrack Richard Kenneth

info2.txt

08967 year1 Edison Bill
06789 year2 Bell Robert Ed

merge.txt

07654 year3 Barrack Richard Kenneth
06789 year2 Bell Robert Ed
08765 year2 Clinton Hilary Claire
08967 year1 Edison Bell
09876 year1 Smith Catee Rose
*/


void readFile( char*, Clist* );
int writeFile( char*, Clist* );
int mergeFiles( char*, char*, char* );


int main() /* ********************* start of main *************************** */
{
    int count1, count2, count3;
    Clist cl;
    init( &cl );

    readFile( fName1, &cl );
    //showAll( &cl );
    msort( &cl );
    count1 = writeFile( "sorted" fName1, &cl );
    if( count1 != cl.size )
        printf( "\nError: only %d of %d lines filed\n", count1, cl.size );
    freeAll( &cl );
    //system( "notepad sorted" fName1 );
    
    readFile( fName2, &cl );
    //showAll( &cl );
    msort( &cl );
    count2 = writeFile( "sorted" fName2, &cl );
    if( count2 != cl.size )
        printf( "\nError: only %d of %d lines filed\n", count2, cl.size );
    freeAll( &cl );
    //system( "notepad sorted" fName2 );
    
    count3 = mergeFiles( "sorted" fName1, "sorted" fName2, fName3 );
    if( count3 != count1+count2 )
        printf( "\nError: only %d of %d lines filed\n", count3, count1+count2);
    system( "notepad " fName3 );
    
    return 0;
    
} /* ******************************* end of main **************************** */


void readFile( char* fName, Clist* cl )
{
    List lst;
    FILE* fp = fopen( fName, "r" );
    if( fp  == NULL )
    {
        printf
        (
            "\nError opening file %s.\n"
            "Perhaps it hasn't been created yet?\n"
            "Press 'Enter' to continue ... ",
            fName
        );
        getchar();
        return;
    }

    /* If the program reaches here ... */
    while( (lst.line = readLine(fp)) )
    {
        push_back( cl, &lst );
    }
    fclose( fp );
    printf("%d records were read into memory from the file %s.\n", cl->size, fName);
}

int writeFile( char *fName, Clist* cl )
{
    FILE* fp;
    int count = 0; /* to track the records actually written to the file */
    pList pL;
    if( !cl->size )
    {
        puts("\nNo records available ... so NO records written to file.") ;
        return 0;
    }

    fp = fopen( fName, "w" );
    if( fp == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", fName);
        getchar();
        return 0;
    }

    for( pL = cl->start; pL != NULL; pL = pL->next )
    {
        fprintf( fp, "%s\n", pL->line );
        ++count;
    }
    fclose( fp );
    return count; /* Number of records written. */
}

int mergeFiles( char* a, char* b, char* c )
{
    FILE* f1, * f2, * f3;
    char* line1, * line2;
    int count = 0;

    f1 = fopen( a, "r" );
    if( f1 == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", a);
        getchar();
        return 0;
    }

    f2 = fopen( b, "r" );
    if( f2 == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", b);
        getchar();
        return 0;
    }
    
    f3 = fopen( c, "w" );
    if( f3 == NULL )
    {
        printf("\nError opening file %s.  Press 'Enter' to continue ... ", c);
        getchar();
        return 0;
    }
    
    line1 = readLine(f1);
    line2 = readLine(f2);
    while( line1 && line2 )
    {
        if( strcmp(line1+sort_offset, line2+sort_offset) <= 0 )
        {
            fprintf( f3, "%s\n", line1 );
            ++count;
            free( line1 );
            line1 = readLine(f1);
        }
        else
        {
            fprintf( f3, "%s\n", line2 );
            ++count;
            free( line2 );
            line2 = readLine(f2);
        }
    }
    
    /* test end conditions ... */
    if( line1 )
    {
        fprintf( f3, "%s\n", line1 );
        ++count;
        free( line1 );
        while( (line1 = readLine(f1)) )
        {
            fprintf( f3, "%s\n", line1 );
            ++count;
            free( line1 );
        }
    }
    else if( line2 )
    {
        fprintf( f3, "%s\n", line2 );
        ++count;
        free( line2 );
        while( (line2 = readLine(f2)) )
        {
            fprintf( f3, "%s\n", line2 );
            ++count;
            free( line2 );
        }
    }
    fclose(f3); fclose(f2); fclose(f1);
    return count;
}


The above program uses these next 2 files also:

ClistOfString.h and readLine.h  ... (that were suppled above, just before this program.)
Title: Re: New thread especially for students of C and C++
Post by: David on July 26, 2010, 08:02:03 AM
Addendum:

Added here ... linked_list_ex5.cpp upgraded ... now as a C++ class

Code: [Select]
// linked_list_ex6.cpp // class MyStudentList //

// linked_list_ex5.cpp upgraded ... now as a C++ class

// this version  2010-07-26 //

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class MyStudentList
{
public:
    // a friend function is used here for this overloaded operator <<
    friend ostream& operator<< ( ostream& fout, MyStudentList& obj )
    {
        if( obj.head == 0 )
            return fout << "The list is empty.\n";
        // else ...
        int i = 0;
        Node* pNext = obj.head;
        while( pNext )
        {
            fout << "Node[" << ++i << "] is holding "
                 << "id[" << pNext->id << "] and name "
                 << pNext->name << endl;
            pNext = pNext->next;
        }
        return fout;
    }

    // Default constructor ...
    MyStudentList()
    {
        head = tail = 0;
        size = 0;
    }
   
    // Destructor ...
    ~MyStudentList()
    {
        clear();
    }

    int get_size() { return size; }
   
    // methods ...
    void push_back( int new_id, string new_name )
    {
        Node* pNew = new Node;
        pNew->id = new_id;
        pNew->name = new_name;
        pNew->next = NULL;

        if( head != 0 )
        {
            tail->next = pNew;
            tail = pNew;
        }
        else
            head = tail = pNew;
        ++ size;
    }
   
    void push_front( int new_id, string new_name )
    {
        Node* pNew = new Node;
        pNew->id = new_id;
        pNew->name = new_name;
       
        pNew->next = head;
        head = pNew;

        if( tail == 0 )
            tail = pNew;
        ++ size;
    }

    void show()
    {
        if( head == 0 )
        {
            cout << "The list is empty.\n";
            return;
        }

        int i = 0;
        Node* pNext = head;
        while( pNext )
        {
            cout << "Node[" << ++i << "] is holding "
                 << "ID[" << pNext->id << "] "
                 << pNext->name << endl;
            pNext = pNext->next;
        }
    }

    void clear()
    {
        Node* pNext = head;
        while( pNext )
        {
            Node* copy  = pNext;
            pNext = pNext->next;
            delete( copy );
        }
        head = tail = 0;
        size = 0;
    }

private:
    struct Node
    {
        int id;
        string name;
        Node *next;
    };
    Node* head;
    Node* tail;
    int size;
};



int main()
{
    // get some 'name' data ...
    const string names[] = { "Susan", "Sarah", "Joe" };
    const int num_names = sizeof names / sizeof names[0] ;
   
    // construct an empty list ...
    MyStudentList myList;
   
    int id ;
    cout << "After push_back loop ... \n";
    for( id = 0; id < num_names; ++id )
        myList.push_back( id+1, names[id] );

    myList.show();
    cout << "The list size is " << myList.get_size() << endl;


    cout << "\nAfter push_back of visitor ... \n";
    // note id has value num_names now ... at end of above loop
    myList.push_back( ++id, "Guest visitor" );
    // using OVERLOADED <<
    cout << myList;
    cout << "The list size is " << myList.get_size() << endl;

    cout << "\nAfter myList.clear() ... \n";
    myList.clear();
    cout << myList;
    cout << "The list size is " << myList.get_size() << endl;

    cout << "\nAfter push_front loop ... and push_front visitor ...\n";

    for( id = 0; id < num_names; ++id )
        myList.push_front( id+1, names[id] );
    myList.push_front( ++id, "Guest visitor" );
    cout << myList; // using overloaded <<
    cout << "The list size is " << myList.get_size() << endl;
   
   
    cout << "\nPress 'Enter' to continue ... " << flush;
    cin.get();
}
Title: Re: New thread especially for students of C and C++
Post by: David on August 05, 2010, 02:21:55 PM
And ...a vector like container class ... bottom up ... (Note: you may like to move the struct into the class as a private data member ... to better 'hide' all the raw data from a user of this class ... or to make the struct into a class with protected data members, public functions/methods and let classBook inherit class Contact)

Code: [Select]
// aryPointersToStruct.cpp

// class Book ...
// a vector like dynamic array of pointers to
// struct Contacts

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <cctype> // re. tolower ...
#include <cstdlib> // re. qsort array ...
using namespace std;

const char FILE_NAME[] = "Contacts2010.txt";
const int ARY_SIZE = 2; // starting array capacity ...

struct Contacts
{
   string name;
   int area;
   int phone;
};
// to print a contact...
ostream& operator<< ( ostream& os, const Contacts& c )
{
    return os << setw(20) << left << c.name+"," << right
              << " (" << c.area << ") "
              << c.phone/10000 << "-"
              << c.phone%10000;
}

int compareContactsNameIgnoreCase( const void*, const void* );
int compareContacts ( const void*, const void* );

string toCaps( const string& s )
{
    string new_s = s;
    for( int i = s.size()-1; i>= 0; --i )
        new_s[i] = toupper(new_s[i]);
    return new_s;
}


class Book
{
public:
    friend ostream& operator<< ( ostream& os, const Book& b );
    // default constructor ...
    Book() : size(0), capacity(ARY_SIZE)
    {
        ary = new Contacts[capacity];
    }
    // destructor ...
    ~Book()
    {
        delete [] ary;
        size = capacity = 0;
    }
    void enlarge();
    void reserve( int n_cap );
    void takeIn();
    void read();
    void write();
    void edit();
    void del( int index );
    void sortBook();
    void find();
    int get_size() { return size;}
    int get_capacity() { return capacity;}
private:
    Contacts* ary; // ary of pointers to struct Contacts
    int size;
    int capacity;
};

ostream& operator<< ( ostream& os, const Book& b )
{
    for( int i = 0; i < b.size; ++ i )
        os << setw(3)<< i+1 << ": "<< b.ary[i] << endl;
    os << "ary size = " << b.size
       << " and capacity = " << b.capacity << endl;

    return os;
}

void Book::enlarge()
{
    capacity += capacity;
    Contacts* n_ary = new Contacts[capacity];
    for( int i = 0; i < size; ++i )
    {
         n_ary[i].name = ary[i].name;
         n_ary[i].area = ary[i].area;
         n_ary[i].phone = ary[i].phone;
    }
    delete [] ary;
    ary = n_ary;
}

void Book::reserve( int n_cap )
{
     if( n_cap > capacity)
     {
         capacity = (n_cap+1)/2;
         enlarge();
     }
}

void Book::takeIn()
{
    cout << "Enter a blank line to exit ...\n";
    string line, prompt = "Enter name, area phone : ";
    while( cout<<prompt<<flush && getline( cin, line ) && line.size() )
    {
        if( line.find(',') == string::npos )
        {
            cout << "You forgot the 'comma' after the name field ...\n";
            continue;
        }
       
        if( size == capacity )
            enlarge();

        istringstream iss( line );
        getline( iss, ary[size].name, ',' );
        iss >> ary[size].area >> ary[size].phone;
        cout << "You entered " << ary[size].name << " ("
             << ary[size].area << ") "
             << ary[size].phone/10000 << "-"
             << ary[size].phone%10000
             << " Ok (y/n) ? " << flush;
        string reply(1,' ');
getline( cin, reply );
        if( reply[0] == 'y' || reply[0] == 'Y' )
            ++ size;
        else cout << "Try entry again ...\n";
    }
}

void Book::read()
{
     if( size > 0 )
     {
         for( ;; )
         {
             cout << "Exit or Add or Overwrite " << size
                  << " contacts already in memory (e/a/o) ? "
                  << flush;
             string reply( 1, ' ' );
             getline( cin, reply );
             if( reply[0] == 'e' || reply[0] == 'E' ) return;
             if( reply[0] == 'a' || reply[0] == 'A' ) break;
             if( reply[0] == 'o' || reply[0] == 'O' ) { size = 0; break; }
             // else ...
             cout << "Try entry again ...\n";
         }
     }
     
     ifstream fin( FILE_NAME );
     string line;
     while( getline( fin, line ) )
     {
         if( size == capacity ) enlarge();
         istringstream iss( line );
         getline( iss, ary[size].name, ',' );
         iss >> ary[size].area >> ary[size++].phone;
     }
     fin.close();
     cout << size << " contacts were read from file ...\n";
}

void Book::write()
{
    ofstream fout( FILE_NAME );
    if( ! fout )
    {
        cout << "Error: opening file "
             << FILE_NAME << " to write.\n";
        return;
    }
    cout << "File '" << FILE_NAME << "' already exists ...\n"
         << "Are sure you want to overwite this file (y/n) ? "
         << flush;
    string reply( 1, ' ' );
    getline( cin, reply );
    if( reply[0] != 'y' && reply[0] != 'Y' )
    {
        cout << "Overwrite aborted ...\n";
        fout.close();
        return;
    }

    int i;
    for( i = 0; i < size; ++i )
    {
        fout << ary[i].name << ", "
             << ary[i].area << " "
             << ary[i].phone << endl;
    }
    fout.close();
    cout << i << " contacts were written to file.\n";
}

void Book::edit()
{
    int i; // index to edit ...
    for( ;; )
    {
        cout << "Contact number to edit (0 to exit): " << flush;
        string line;
        getline( cin, line );
        istringstream iss( line );
        iss >> i;
        if( i == 0 ) return;
        if( --i >= 0 && i < size )
        {
            cout << ary[i] << endl;
            cout << "Abort, Delete or Edit (a/d/e) ? " << flush;
            string reply( 1, ' ' );
            getline( cin, reply );
            if( reply[0] == 'a' || reply[0] == 'A' ) return;
            if( reply[0] == 'd' || reply[0] == 'D' ) { del( i ); return; }
            if( reply[0] == 'e' || reply[0] == 'E' ) break;
            else cout << "Invalid entry.  Enter one of (a/d/e) ...\n";
        }
        else if( size > 0 )
             cout << "Outside valid range 1.."
                  << size << endl;
        else cout << "No contacts in memory to edit or erase ...\n";
    }
    // ok so can edit now ...
    string line, prompt = "Enter name, area phone : ";
    while( cout << prompt && getline( cin, line ) && line.size() )
    {
        if( line.find(',') == string::npos )
        {
            cout << "You forgot the 'comma' after the name field ...\n";
            continue;
        }
       
        string n_name;
        int n_area, n_phone;
        istringstream iss( line );
        getline( iss, n_name, ',' );
        iss >> n_area >> n_phone;
        cout << "You entered " << n_name << " ("
             << n_area << ") "
             << n_phone/10000 << "-"
             << n_phone%10000
             << " Ok (a/y/n) ? " << flush;
        string reply(1,' ');
        getline( cin, reply );
        if( reply[0] == 'y' || reply[0] == 'Y' )
        {
            ary[i].name = n_name;
            ary[i].area = n_area;
            ary[i].phone = n_phone;
            return;
        }
        else if( reply[0] == 'a' || reply[0] == 'A' )
        {
             cout << "Edit aborted ...\n";
             return;
        }
        else cout << "Try entry again ...\n";
    }
}

void Book::del( int index )
{

    for( int i = index; i < size-1; ++ i )
    {
        ary[i].name = ary[i+1].name;
        ary[i].area = ary[i+1].area;
        ary[i].phone = ary[i+1].phone;
    }
    -- size;
}

void Book::sortBook()
{
    cout << "Ignore case (y/n ) ? " << flush;
    string reply( 1, ' ' );
    getline( cin, reply );
    if( reply[0] == 'y' || reply[0] == 'Y' )
    {
        qsort(ary, size, sizeof(Contacts), compareContactsNameIgnoreCase);
        cout << "Sorted with case ignored ...\n";
    }
    else
    {
        qsort(ary, size, sizeof(Contacts), compareContacts);
        cout << "Sorted with case considered ...\n";
    }
}

void Book::find()
{
    int i; // index to edit ...
    for( ;; )
    {
        startAgain:
        cout << "Contact name to find (empty line to exit): " << flush;
        string line;
        getline( cin, line );
        if( !line.size() ) break;
        for( int j = 0; j < size; ++ j )
        {
            if( ary[j].name == line )
            {
                cout << "Found at position "
                     << j+1 << ": "<< ary[j] << endl;
                goto startAgain;
            }
        }
        // else ...
        cout << "Not found ...\n";
        for( int j = 0; j < size; ++ j )
        {
            if( toCaps(ary[j].name).find(toCaps(line)) != string::npos )
            {
                cout << "but found in .. "
                     << j+1 << ": "<< ary[j] << endl;
            }
        }
    }
}


const string MENU = "1. (T)ake in new contacts from keyboard\n"
                    "2. show (A)ll contacts in array memory\n"
                    "3. (R)ead in all contacts in file\n"
                    "4. (W)rite all contacts to file\n"
                    "5. (E)dit/(E)rase a contact\n"
                    "6. (S)ort contacts\n"
                    "7. (F)ind contact\n"
                    "8. e(X)it\n"
                    "Enter your choice (1..8) : ";

int main()/////////////////////////////////////////////////
{
    Book myBlackBook;

    // testing size and capacity ...
    cout << "ary size = " << myBlackBook.get_size()
         << " and capacity = " << myBlackBook.get_capacity()
         << endl;
/*
    myBlackBook.reserve( 200 );
    cout << "size = " << myBlackBook.get_size()
         << " capacity = " << myBlackBook.get_capacity()
         << endl;
*/
    myBlackBook.read();

    int choice;
    do
    {
        cout << myBlackBook << endl;
        cout << MENU << flush;
        choice = cin.get();
        cin.sync();
        switch( choice )
        {
            case '1': case 't': case 'T' : myBlackBook.takeIn(); break;
            case '2': case 'a': case 'A' : /*cout << myBlackBook;*/ break;
            case '3': case 'r': case 'R' : myBlackBook.read(); break;
            case '4': case 'w': case 'W' : myBlackBook.write(); break;
            case '5': case 'e': case 'E' : myBlackBook.edit(); break;
            case '6': case 's': case 'S' : myBlackBook.sortBook(); break;
            case '7': case 'f': case 'F' : myBlackBook.find(); break;
            case '8': case 'x': case 'X' : choice = '8'; break;
            default  : cout << "Not implemented yet ...\n";
        }
    }
    while( choice != '8' );
}//////////////////////////////////////////////////////////


int compareContactsNameIgnoreCase( const void* s1, const void* s2 )
{
    Contacts* c1 = (Contacts*) s1;
    Contacts* c2 = (Contacts*) s2;
    int i = 0;
    while( i != c1->name.size() && i != c2->name.size()  )
    {
        if( tolower(c1->name[i]) != tolower(c2->name[i]) )
            break;
        ++i;
    }
    /* now test end conditions .... */
    if( i == c1->name.size() && i == c2->name.size() )
    {
        if( c1->area == c2->area ) return c1->phone - c2->phone;
        else return c1->area - c2->area;
    }
    else if( i < c1->name.size() && i < c2->name.size() )
         return tolower(c1->name[i]) - tolower(c2->name[i]);
    else if( i < c2->name.size() ) return -1;
    else return 1;
}

int compareContacts ( const void* s1, const void* s2 )
{
     Contacts* c1 = (Contacts*) s1;
     Contacts* c2 = (Contacts*) s2;
     if( c1->name == c2->name )
     {
         if( c1->area == c2->area )
         {
             return c1->phone - c2->phone;
         }
         else return c1->area - c2->area;;

     }
     else if (c1->name < c2->name) return -1;
     else return 1;
}
Title: Re: New thread especially for students of C and C++
Post by: David on September 13, 2010, 12:09:59 PM
A start of a Money class ... with change ...


Code: [Select]
// classMoney.cpp // this revision 2010-09-12 //

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

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <vector>

using namespace std;

class Money
{
public:
    friend ostream& operator << ( ostream&, const Money& );
    friend Money operator + ( const Money&, const Money& );
    friend Money operator %( double, const Money & ); // get percent ...
    Money() : cents(0) {} // default constructor ...
    Money( int cnts ) : cents( cnts ) {}
    Money (double dols ) : cents( int( (dols+.005)*100 )) {}
    int get_cents() { return cents; }
    void show_change();
private:
    int cents; // input in main restricts to 9 digits for cents (7 for dollars)
    // Note: you could upgrade here to a long long for (total) cents ...
    // if 7 digits is not enough for dollars ...
};

ostream& operator << ( ostream& out, const Money& m )
{   // convert to string ...
    ostringstream oss;
    oss << m.cents;
    string sm = oss.str();
    // and format ...
    if( sm.size() > 2 ) sm.insert( sm.size()-2, "." );
    else if( sm.size() == 2 ) sm = "0." + sm;
    else sm = "0.0" + sm;
    return out << sm;
}

Money operator + ( const Money& a, const Money& b )
{
    Money c;
    c.cents = a.cents + b.cents;
    return c;
}

Money operator %( double pc, const Money& m) // get percent ...
{
    Money c;
    c.cents =  int(( m.cents)* pc/100.0 + 0.5 );
    return c;
}

void Money::show_change()
{
    int cents_remaining = cents;

    int dols = cents_remaining / 100;
    cents_remaining -= dols*100;

    int qrts = cents_remaining / 25;
    cents_remaining -= qrts*25;

    int dimes = cents_remaining / 10;
    cents_remaining -= dimes*10;

    int nickles = cents_remaining / 5;
    cents_remaining -= nickles*5;

    cout << "change is ..."
         << "\ndollars  : " << dols
         << "\nquarters : " << qrts
         << "\ndimes    : " << dimes
         << "\nnickles  : " << nickles
         << "\npennies  : " << cents_remaining << endl;
}


// used by main ...

bool more();/* defaults to 'true'/'yes' ... unless 'n' or 'N' entered ... */
bool isValidInt( const string& s );
bool getInput( const string& prompt, int& cents );



int main() /////////////////////////////////////////////////////////////////////
{
    cout << "Money(1.235)  +  Money(99.7249)  = "
         << Money(1.235) << " + " <<  Money(99.72) << " = "
         << Money(1.235) + Money(99.72) << endl << endl;
         
    cout << "13 % Money(20.255) = "
         << .13 << " * " <<  Money(20.255) << " = "
         << 13 % Money(20.255) << endl << endl;

    do
    {
        vector< Money > mv;
        do
        {
            int cents;
            if( !getInput( "Enter amount: ", cents )) continue;
            mv.push_back( Money( cents ));
        }
        while( more() );

        Money sum;
        cout << "The sum of\n";
        for( int i = mv.size()-1; i >= 0; --i )
        {
            cout << setw(10) << mv[i] << endl;
            sum = sum + mv[i];
        }
        cout << "==========\n"
             << setw(10) << sum << endl;
           

        cout << "Or " << sum.get_cents() << " all in cents.\n";
       
        Money HST = 13.0 % sum;
        cout << "The new Ontario HST is " << setw(10) << HST << endl
             << "and the new total is   " << setw(10) << sum + HST << endl
             << "or in ";
        (HST+sum).show_change();
       
        /* make sure cin stream is 'empty' before calling 'more()' ... */
    }
    while( more() );
} //////////////////////////////////////////////////////////////////////////////



bool more()/* defaults to 'true'/'yes' ... unless 'n' or 'N' entered ... */
{
    cout << "More (y/n) ? " << flush;
    int reply = cin.get();
    cin.sync(); /* 'flush' cin stream  ...  */
    if( reply == 'n' || reply == 'N' ) return false;
    /* else ... */
    return true;
}

bool isValidInt( const string& s )
{
    for( int i = s.size()-1; i >= 0; --i )
        if( s[i] < '0' || s[i] > '9' ) return false;
    return true;
}

bool getInput( const string& prompt, int& cents )
{
    cout << prompt << flush;
    string testStr;
    getline( cin, testStr );

    // see if test string entered above has a decimal point ...
    size_t i;
    if( (i = testStr.find('.')) != string::npos ) // it has a decimal point
    {
        string cnts = testStr.substr( i+1 );
        if( !isValidInt( cnts ) ) // validate cents part ...
        {
            cout << cnts << " is not an integer cents amount...\n";
            return false;
        }
        // else ...
        if( cnts.size() < 2 ) cnts += "0";

        istringstream iss_c( cnts.substr( 0, 2) );
        iss_c >> cents;
        if( cnts.size() > 2 && cnts[2] >= '5' ) ++ cents;

        // now handle any dollars ... but validate first ...

        testStr.erase( i ); // only dollars  remain now ... if any ?
        if( testStr == "" ) testStr = "0";
        // limit is here set to total of 9 digits for cents ...
        if( !isValidInt( testStr ) || testStr.size() > 7 )
        {
            cout << testStr << " is not a valid dollar amount here ...\n";
            return false;
        }
        // else ...
        istringstream iss_d( testStr );
        int dlrs;
        if( iss_d >> dlrs )  cents += dlrs * 100;
        else { cout << iss_d << " invalid dollar amount ...\n"; return false; }
    }
    else // NO decimal point in test string entered ... so ... validate if int
    {   // limit is here set to total of 9 digits for cents ...
        if( !isValidInt( testStr ) || testStr.size() > 7 )
        {
            cout << testStr << " is not a valid dollar amount here ...\n";
            return false;
        }
        // else ...
        istringstream iss_d( testStr );
        int dlrs;
        if( iss_d >> dlrs )  cents = dlrs * 100;
        else { cout << testStr << " invalid dollar amount ...\n"; return false; }
    }
    return true;
}