Author Topic: New thread especially for students of C and C++  (Read 98023 times)

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #30 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 ...
« Last Edit: April 09, 2010, 08:13:34 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #31 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. */
}
« Last Edit: May 26, 2010, 09:16:47 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #32 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 );
}
« Last Edit: May 17, 2010, 02:51:23 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #33 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
« Last Edit: April 11, 2010, 06:45:21 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #34 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.)


« Last Edit: August 05, 2010, 03:58:06 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #35 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 ===>*/
« Last Edit: October 09, 2016, 10:26:03 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #36 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;
}
« Last Edit: May 16, 2010, 06:37:50 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #37 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
« Last Edit: July 31, 2011, 12:17:38 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #38 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
« Last Edit: August 05, 2010, 02:10:16 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #39 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();
}

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #40 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
« Last Edit: May 26, 2010, 09:26:52 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #41 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
« Last Edit: May 04, 2010, 11:07:04 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #42 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();
}
« Last Edit: July 14, 2010, 06:18:24 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #43 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)
« Last Edit: May 25, 2013, 08:12:36 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: New thread especially for students of C and C++
« Reply #44 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 ...
« Last Edit: May 08, 2010, 06:41:31 AM by David »