Using malloc() & qsort with a union revisited

This is a discussion on Using malloc() & qsort with a union revisited within the C Programming forums, part of the General Programming Boards category; Hi, I'm still having problems with my program. This is what the program should do: There is a file which ...

  1. #1
    Registered User
    Join Date
    Aug 2001
    Posts
    20

    Angry Using malloc() & qsort with a union revisited

    Hi,

    I'm still having problems with my program.

    This is what the program should do:

    There is a file which contains records which can be of 3 varying sizes. 'type' and 'code' are common to all 3 records. The program should allocate some memory for the records using malloc() and then sort the records using the 'code'. I have chosen the qsort function to do this.

    The 'code' is declared as type char and consists of 4 numeric digits and a check digit on the end which could be an 'X' e.g. 2349X.

    Thanks to Salem for the idea of using a typedef struct, however can I do this as one of the structures only consists of a 'type' and 'code'? e.g.

    #define REC_SIZE ((int)(sizeof typedef struct))

    struct ir_record{
    char part_no [7];
    char quantity [5];
    };

    struct c_record{
    char name [21];
    char address [61];
    char balance [10];
    char limit [8];
    };

    union customer{
    struct ir_record old;
    struct c_record create;
    };

    typedef struct{
    char type;
    char code[6];
    union customer details; /* the third record 'd_record' */
    }; /*doesn't have any other part*/
    /*only 'type' and 'code', therefore*/
    /*will this still work or do I have to*/
    /*revert back to using 3 structures*/
    /*and a union - if so how?*/

    FILE *validfile_ptr;

    void main()
    {
    typedef struct paint, *a_rec_ptr, *m_ptr;
    unsigned long int no_of_recs, position; /*used to find info*/
    a_rec_ptr = &paint; /*needed for qsort*/
    m_ptr = a_rec_ptr;

    /*some malloc() code should go here*/

    qsort(m_ptr, no_of_recs, REC_SIZE, compare);

    };


    Also I am having a problem with the 'compare' function:

    int compare(const void *a, const void *b)
    {
    return(strcmp((typedef struct*)a)->code, ((typedef struct*)b
    ->code);

    /*I was told that I should use strcmp, is this because of the possibility of an 'X' on the end of the 'code'? Also what is the 'compare' doing? - books just don't give enough info. on it.

    Please help

    Lynds

  2. #2
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,505
    Well you're overdoing your use of the typedef keyword. It's only for renaming some existing type.

    #define REC_SIZE ((int)(sizeof typedef struct))
    should be
    #define REC_SIZE (sizeof(cust_rec_st))

    typedef struct {
      char type;
      char code[6];
      union customer details;
    } cust_rec_st;

    > /* the third record 'd_record' */....
    No, this is just fine.

    > void main()
    main returns an int

    > typedef struct paint, *a_rec_ptr, *m_ptr;
    Not sure what your other variables are, but you need something like
      cust_rec_st *m_ptr;

    > return(strcmp((typedef struct*)a)->code, ((typedef struct*)b
    ->code);
    It's easier if you cast the pointers first, especially when your compare functions get more complicated.

    int compare(const void *a, const void *b) {
      const cust_rec_st *pa = a;
      const cust_rec_st *ba = b;
      return strcmp( pa->code, pb->code );
    }

    > is this because of the possibility of an 'X' on the end of the 'code'?
    It's because code is a char array (and not an int). The 'X' at the end means that you can't store it as an int.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  3. #3
    Registered User
    Join Date
    Aug 2001
    Posts
    20

    Thumbs up

    Excellent, I'm much less frustrated now.

    However, saying that I'm stuck with malloc() - books just don't go far enough.

    So far I have:

    FILE *validfile_ptr;

    cust_rec_st *cust_rec_ptr, *m_ptr;

    cust_rec_ptr = (cust_rec_st*)malloc(no_of_recs x REC_SIZE);

    m_ptr = cust_rec_ptr;

    /*now here's the question - I'm using a binary file so could I*/
    /*use fwrite as follows?*/

    fwrite(m_ptr, REC_SIZE, no_of_recs, validfile_ptr);

    /*records now supposedly all in area of memory?*/

    m_ptr = cust_rec_ptr; /*put pointer to first record in area*/
    /*of memory*/

    qsort(m_ptr, no_of_recs, REC_SIZE, compare);

    If not fwrite what's the best option? fgets()?

    Thanks
    Lynds

  4. #4
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,505
    > FILE *validfile_ptr;
    If you want to save binary data, then do
      validfile_ptr = fopen( "file.bin", "wb" );

    > cust_rec_ptr = (cust_rec_st*)malloc(no_of_recs x REC_SIZE);
      cust_rec_ptr = malloc(no_of_recs * REC_SIZE);
    The cast isn't necessary if you've included stdlib.h, and this is C (and not C++).

    > m_ptr = cust_rec_ptr;
    You don't need to do this, just to call fwrite and qsort. Both will work if you use cust_rec_ptr

    > fwrite(m_ptr, REC_SIZE, no_of_recs, validfile_ptr);
    Yes.
    You could check that fwrite returns the same value as no_of_recs
    int status = fwrite(m_ptr, REC_SIZE, no_of_recs, validfile_ptr);
    if ( status == no_of_recs ) {
      // success
    } else {
      // error
    }

    qsort call looks good as well
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  5. #5
    Registered User
    Join Date
    Aug 2001
    Posts
    20

    Lightbulb Same program

    Nearly there - here's another question.

    Say I not only need the program to sort the codes into order, I also need to sort any identical codes into order of 'type' (also common to all 3 records).

    There are I, R, D & C record types. 'C' records need to come last after the other 3. Therefore is the following code correct, as the ASCII code for 'C' (67) is less than the other 3.

    int compare(const void *a, const void *b)
    {
    const cust_rec_st *pa = a;
    const cust_rec_st *ba = b;

    if(strcmp(pa->code, ba->code) > 0)
    {
    return 1; /*pa greater than ba*/
    }
    else
    {
    if(strcmp(pa->code, ba->code) < 0)
    {
    return -1; /*pa less than ba*/
    }
    else
    {
    if(pa->type < ba->type) /*codes equal so use types*/
    {
    return 1;
    }
    else
    {
    return -1;
    }
    }


    Is my understanding completely off, or will this work?

    Thanks
    Kitten

  6. #6
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,505
    Pretty close

    Since you can't use the record type code directly, use another function to turn the record type code into some number which you can compare.

    This means you're not dependent on the type codes you choose. Changing the sorting order for type codes is just one simple change to the order[] array, and you're done.

    Code:
    #include <string.h>
    
    /* given a record type character, return a value indicating its sort order */
    int type_order ( char c ) {
        static char order[] = "IRDC";
        return strchr(order,c) - order;
    }
    
    int compare(const void *a, const void *b) {
        const cust_rec_st *pa = a;
        const cust_rec_st *ba = b;
        if ( strcmp( pa->code, ba->code ) > 0 ) {  
            return 1;       /*pa greater than ba*/
        } else
        if ( strcmp( pa->code, ba->code ) < 0 ) {
            return -1;      /*pa less than ba*/
        } else {
            int t1 = type_order( pa->type );
            int t2 = type_order( pb->type );
            if ( t1 < t2 ) return -1;
            if ( t1 > t2 ) return +1;
            return 0;
        }
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  7. #7
    Registered User
    Join Date
    Aug 2001
    Posts
    20

    Question Why won't it work?

    The program should do what is already specified in my above postings, and then print the newly sorted file under appropriate headings. There are 134 records that should have been sorted.

    I sat down to run it expecting it to work but all I got printed out was the headings and:
    1 Records Reported
    0 Issues
    0 Receipts
    0 Deletions
    0 Creations (all completely wrong).

    I have spent the last 2 hours trying to find the problem - is it something to do with both files being binary? or reading each record into the structure to print them?

    Help!! My program is attached below.
    Attached Files Attached Files

  8. #8
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,505
    > is it something to do with both files being binary?
    Probably not

    > or reading each record into the structure to print them?
    Except there is no code to read the files !!!!
    So trying to print them out is a loser

    You just have
    > qsort(m_ptr, no_of_recs, REC_SIZE, compare);
    > ...
    > if((fwrite(m_ptr, REC_SIZE, no_of_recs, sortedfile_ptr)) != no_of_recs)
    So you're sorting random memory (as returned by malloc), then writing it out to a file.

    > print_sorted_file(cust_rec_ptr, sortedfile_ptr);
    Well it's OK as a function call, but this inside it is a problem

    > fread(STR_ptr, REC_SIZE, 1, sorted);
    sorted is a FILE* to a file opened for writing (not reading)

    In fact, the whole sortedfile is bogus - all the data you want, is already in memory via the cust_rec_ptr to the allocated memory (assuming you've read stuff in and sorted it).
    So all you need really is a for loop to access this data as an array
    Code:
    for ( i = 0 ; i < n ; i++ ) {
        switch ( STR_ptr[i].type ) {
            // etc
        }
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  9. #9
    Registered User
    Join Date
    Aug 2001
    Posts
    20

    Question

    Ooops!!
    In typing my program into the compiler I missed a line (to read data into memory:- fread(m_ptr, REC_SIZE, no_of_recs, validfile_ptr)). Rather important really.

    I agree completely, it would make more sense to print straight from memory, however I have got to sort the records in memory then read them into the sortedfile, then read each record in turn from this sortedfile and print it.

    I have changed the mode of the sortedfile_ptr to "w+b" for reading and writing (silly mistake).

    I've run my program again and it is only printing ONE record out of 134. Using the Trace into I have found it goes once through the loop, while(!feof(sorted)), then when it loops back it seems to find end of file and breaks out of the loop. Why is this? Has only one record been read into the memory or only one record read into the sorted file from memory?
    I put a watch on the no_of_recs variable and it stays at 134.

    Thanks

  10. #10
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,505
    Did you rewind the file between writing and reading (or otherwise move the file pointer back to the beginning of the file) ?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  11. #11
    Registered User
    Join Date
    Aug 2001
    Posts
    20

    Smile

    Oh, you are very good!!!!
    I had forgotten to rewind the sortedfile_ptr.
    It now prints.

    Thankyou

  12. #12
    Registered User
    Join Date
    Nov 2001
    Posts
    18

    Same assignment

    Dear Cyber Kitten

    We are doing the same assigment and I am having difficulty with writting the sorted data to the bianry file.

    Can we email each other ?

    stephanos@writeme.com


    Stephanos

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  2. Still stuck using qsort + malloc!!
    By VegasSte in forum C Programming
    Replies: 16
    Last Post: 10-16-2002, 03:34 PM
  3. Need help with qsort() and union of structures
    By DanTheMan in forum C Programming
    Replies: 1
    Last Post: 04-02-2002, 07:30 AM
  4. Union In Malloc Sort
    By Delboy in forum C Programming
    Replies: 0
    Last Post: 10-16-2001, 06:15 PM
  5. Using malloc() & qsort with a union
    By Cyber Kitten in forum C Programming
    Replies: 2
    Last Post: 09-02-2001, 07:57 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21