Desktop Programming > C/C++ & Visual C++

New thread especially for students of C and C++

(1/11) > >>

David:
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 . . .

--- End quote ---

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)
or here ...
BEGINNING COMPUTER PROGRAMMING (using HLA and Python)
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)
... 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

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)

David:
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: ---/*
    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;
}

--- End code ---


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: ---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;

--- End code ---


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



--- Code: ---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;

--- End code ---

David:
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: ---/* 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;
}

--- End code ---

David:
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: ---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;

--- End code ---


Or just a record ...



--- Code: ---type
Vector:
record
x: real32;
y: real32;
endrecord;

--- End code ---


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



--- Code: ---typedef struct Date 
{
   int  mm,
        dd,
        yy;
}Date;

--- End code ---


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: ---/*
    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');
}
--- End code ---


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



--- Code: ---/*
    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.

*/
--- End code ---


A simple approach to 'Dates' in C++ is given below ... that would not fit in this page.

admin:
Thanks David for all your efforts.

Navigation

[0] Message Index

[#] Next page

Go to full version