Thread: qsort Problems to mount the compare function

  1. #1
    Registered User
    Join Date
    Nov 2018
    Posts
    32

    qsort Problems to mount the compare function

    Good afternoon everyone!

    I would like not to use the struct identifier to make the qsort work
    Note: I also do not want to use typedef

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //struct IDDatabase { // I do not want to declare it!
    struct { // I want my struct declared so!
       char Col1[10],
            Col2[10],
            Col3[10];
    } Database[] = { {"carla" , "deia"  , "m"},
                     {"brenda", "lilia" , "h"},
                     {"amanda", "karol" , "c"},
                     {"amanda", "july"  , "b"},
                     {"brenda", "debora", "a"}};
    
    int Compare(const void *Elemento1, const void *Elemento2) {
       //return strcmp(((struct IDDatabase*)Elemento1)->Col1, ((struct IDDatabase*)Elemento2)->Col1); // WORK
    
       How can I change the line up if my struct does not have a declared identifier or typedef?
    }
    
    int main() {
       qsort(Database, 5, sizeof Database[0], Compare);
    
       for(int Reg = 0; Reg < 5; Reg++)
          printf("%s\t|%s\t|%s\n", Database[Reg].Col1, Database[Reg].Col2, Database[Reg].Col3);
    }
    Thank you all you can help!
    Last edited by marcelo.br; 02-05-2022 at 02:15 PM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,122
    Well why don't you want to do those things?

    There seems to be zero cost in making your code very easy to write.

    I suppose in this special case, you can pretend you have char Col[3][10]
    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.

  3. #3
    Registered User
    Join Date
    Nov 2018
    Posts
    32
    Quote Originally Posted by Salem View Post
    Well why don't you want to do those things?
    There seems to be zero cost in making your code very easy to write.
    It's more a situation on how to do it! Nothing special!

    Quote Originally Posted by Salem View Post
    I suppose in this special case, you can pretend you have char Col[3][10]
    In my case, my interest is in a Struct!

    Believing it will be possible to do this without declaring an identifier or typedef

  4. #4
    Registered User
    Join Date
    Feb 2022
    Posts
    29
    Quote Originally Posted by marcelo.br View Post
    It's more a situation on how to do it! Nothing special!


    In my case, my interest is in a Struct!

    Believing it will be possible to do this without declaring an identifier or typedef
    OK, so what you are saying (if I understand correctly) is that you want to use a single array of a struct declared with an anonymous type (e.g., no typedef), and be able to manipulate it in the function Compare().

    The problem is that, as you note yourself, that Compare() needs a way to cast the structures from void*, which requires some sort of data type. Correct?

    EDIT: I tried compiling the example code in GCC 11.1.0, and got the following error:

    Code:
    anon-struct.c:10:14: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘=’ token   
       10 | } Database[] = { {"carla" , "deia"  , "m"},
          |              ^
    It looks as if trying to initialize an array of a data structure this way doesn't actually work, at least not in the current version of GCC. Just why this is the case isn't clear to me.
    Last edited by Schol-R-LEA-2; 02-05-2022 at 07:13 PM.

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,122
    > Believing it will be possible to do this without declaring an identifier or typedef
    The short answer would seem to be you're out of luck.
    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.

  6. #6
    Registered User
    Join Date
    Nov 2018
    Posts
    32
    Quote Originally Posted by Schol-R-LEA-2 View Post
    OK, so what you are saying (if I understand correctly) is that you want to use a single array of a struct declared with an anonymous type (e.g., no typedef), and be able to manipulate it in the function Compare().

    The problem is that, as you note yourself, that Compare() needs a way to cast the structures from void*, which requires some sort of data type. Correct?

    EDIT: I tried compiling the example code in GCC 11.1.0, and got the following error:

    Code:
    anon-struct.c:10:14: error: expected :, ,, ;, } or __attribute__ before = token   
       10 | } Database[] = { {"carla" , "deia"  , "m"},
          |              ^
    It looks as if trying to initialize an array of a data structure this way doesn't actually work, at least not in the current version of GCC. Just why this is the case isn't clear to me.

    See: GCC works with anonymous struct! Your compilation refers that you forgot any punctuation! (gcc v.9.2.0 or superior)


    You can compile even by checking all errors: gcc test.c -o test -O3 -Wall -pedantic -pedantic-errors -Werror

    My problem is that I want to use qsort in the same code! Without adding identifier or typedef to struct

    Below is a simple code running!
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct {
       char Col1[10],
            Col2[10],
            Col3[10];
    } Database[] = { {"carla" , "deia"  , "m"},
                     {"brenda", "lilia" , "h"},
                     {"amanda", "karol" , "c"},
                     {"amanda", "july"  , "b"},
                     {"brenda", "debora", "a"}};
    
    int main() {
       for(int Reg = 0; Reg < 5; Reg++)
          printf("%s\t|%s\t|%s\n", Database[Reg].Col1, Database[Reg].Col2, Database[Reg].Col3);
    }
    Last edited by marcelo.br; 02-06-2022 at 07:43 AM.

  7. #7
    Registered User
    Join Date
    Nov 2018
    Posts
    32
    Quote Originally Posted by Salem View Post
    > Believing it will be possible to do this without declaring an identifier or typedef
    The short answer would seem to be you're out of luck.
    I understand what it says! I just want to have 100% sure if it's possible or not!

  8. #8
    Registered User
    Join Date
    Feb 2022
    Posts
    29
    I see what I did wrong, so I retract the last part of my previous comment.

    More importantly, I tried this and it did work:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    
    struct {
      char Col1[10],
           Col2[10],
           Col3[10];
    } Database[] =  { {"carla" , "deia"  , "m"},
                      {"brenda", "lilia" , "h"},
                      {"amanda", "karol" , "c"},
                      {"amanda", "july"  , "b"},
                      {"brenda", "debora", "a"}};
    
    
    int Compare(const void *Elemento1, const void *Elemento2) {
      return strcmp(
                    ((struct {
                      char Col1[10],
                           Col2[10],
                           Col3[10];
                    } *)Elemento1)->Col1, 
                    ((struct {
                      char Col1[10],
                           Col2[10],
                           Col3[10];
                    } *)Elemento2)->Col1);
    }
    
    
    int main() {
      qsort(Database, 5, sizeof Database[0], Compare);
    
    
      for(int Reg = 0; Reg < 5; Reg++)
        printf("%s\t|%s\t|%s\n", Database[Reg].Col1, Database[Reg].Col2, Database[Reg].Col3);
    }
    I am still at a loss as to why you wanted to do such an ugly thing, but it is possible. I assume that this is mainly to quench your curiosity, not something you plan to use in production code.

    On a side note, I am assuming that this is an example version of the problem, as using opaque names such as col1, col2, etc. would be a bad practice. Perhaps something like this would make more sense:

    Code:
    struct {
      char Personal_name[10],
           Middle_name[10],
           Surname_initial[10];
    } Database[] =  { {"carla" , "deia"  , "m"},
                      {"brenda", "lilia" , "h"},
                      {"amanda", "karol" , "c"},
                      {"amanda", "july"  , "b"},
                      {"brenda", "debora", "a"}};

    As said earlier, though, using a 3-dimensional array would be easier overall:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
     char Database[][3][10] =  { {"carla" , "deia"  , "m"},
                                 {"brenda", "lilia" , "h"},
                                 {"amanda", "karol" , "c"},
                                 {"amanda", "july"  , "b"},
                                 {"brenda", "debora", "a"}};
    
    
    int Compare(const void *Elemento1, const void *Elemento2) {
      return strcmp(((char *)Elemento1), ((char *)Elemento2));
    }
    
    
    int main() {
      qsort(Database, 5, sizeof Database[0], Compare);
    
    
      for(int Reg = 0; Reg < 5; Reg++)
        printf("%s\t|%s\t|%s\n", Database[Reg][0], Database[Reg][1], Database[Reg][2]);
    }
    I know you said you needed it to be a struct, but you never said why.
    Last edited by Schol-R-LEA-2; 02-06-2022 at 10:22 AM.

  9. #9
    Registered User
    Join Date
    Feb 2019
    Posts
    963
    What is the problem to do something like this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define ARRAY_ELEMENTS(a) ( sizeof (a) / sizeof (a)[0] )
    
    struct record
    {
      char *col1, *col2, *col3;
    };
    
    static int cmp_ ( const void *a, const void *b )
    {
      const struct record *p, *q;
      int result;
    
      // Pointers aliasing...
      p = a;
      q = b;
    
      // A little "plus" here.. sort by first, second and third column,
      // if necessary...
      result = strcmp ( p->col1, q->col1 );
      if ( ! result ) result = strcmp( p->col2, q->col2 );
      if ( ! result ) result = strcmp( p->col3, q->col3 );
    
      return result;
    }
    
    int main( void )
    {
      static struct record records[] =
      {
        { "carla", "deia", "m" },
        { "brenda", "lilia", "h" },
        { "amanda", "karol", "c" },
        { "amanda", "july", "b" },
        { "brenda", "debora", "a" }
      };
    
      // Original order...
      puts( "Original:" );
      for ( int i = 0; i < ARRAY_ELEMENTS ( records ); i++ )
        printf ( "\t%s\t|%s\t|%s\n", 
          records[i].col1, records[i].col2, records[i].col3 );
      
      qsort ( records, 
              ARRAY_ELEMENTS ( records ), sizeof records[0], 
              cmp_ );
    
      // Sorted.
      puts( "\nSorted:" );
      for ( int i = 0; i < ARRAY_ELEMENTS ( records ); i++ )
        printf ( "\t%s\t|%s\t|%s\n",
          records[i].col1, records[i].col2, records[i].col3 );
    }
    Last edited by flp1969; 02-06-2022 at 11:34 AM.

  10. #10
    Registered User
    Join Date
    Nov 2018
    Posts
    32
    Quote Originally Posted by flp1969 View Post
    What is the problem to do something like this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define ARRAY_ELEMENTS(a) ( sizeof (a) / sizeof (a)[0] )
    
    struct record
    {
      char *col1, *col2, *col3;
    };
    
    static int cmp_ ( const void *a, const void *b )
    {
      const struct record *p, *q;
      int result;
    
      // Pointers aliasing...
      p = a;
      q = b;
    
      // A little "plus" here.. sort by first, second and third column,
      // if necessary...
      result = strcmp ( p->col1, q->col1 );
      if ( ! result ) result = strcmp( p->col2, q->col2 );
      if ( ! result ) result = strcmp( p->col3, q->col3 );
    
      return result;
    }
    
    int main( void )
    {
      static struct record records[] =
      {
        { "carla", "deia", "m" },
        { "brenda", "lilia", "h" },
        { "amanda", "karol", "c" },
        { "amanda", "july", "b" },
        { "brenda", "debora", "a" }
      };
    
      // Original order...
      puts( "Original:" );
      for ( int i = 0; i < ARRAY_ELEMENTS ( records ); i++ )
        printf ( "\t%s\t|%s\t|%s\n", 
          records[i].col1, records[i].col2, records[i].col3 );
      
      qsort ( records, 
              ARRAY_ELEMENTS ( records ), sizeof records[0], 
              cmp_ );
    
      // Sorted.
      puts( "\nSorted:" );
      for ( int i = 0; i < ARRAY_ELEMENTS ( records ); i++ )
        printf ( "\t%s\t|%s\t|%s\n",
          records[i].col1, records[i].col2, records[i].col3 );
    }
    I think you did not read the question! Your code is full of identifiers!
    And between the example I showed compared to yours, mine is much simpler, if I wanted to use identifier!

  11. #11
    Registered User
    Join Date
    Nov 2018
    Posts
    32

    Thumbs up

    Quote Originally Posted by Schol-R-LEA-2 View Post
    I see what I did wrong, so I retract the last part of my previous comment.

    More importantly, I tried this and it did work:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct {
      char Col1[10],
           Col2[10],
           Col3[10];
    } Database[] =  { {"carla" , "deia"  , "m"},
                      {"brenda", "lilia" , "h"},
                      {"amanda", "karol" , "c"},
                      {"amanda", "july"  , "b"},
                      {"brenda", "debora", "a"}};
    
    int Compare(const void *Elemento1, const void *Elemento2) {
      return strcmp(
                    ((struct {
                      char Col1[10],
                           Col2[10],
                           Col3[10];
                    } *)Elemento1)->Col1, 
                    ((struct {
                      char Col1[10],
                           Col2[10],
                           Col3[10];
                    } *)Elemento2)->Col1);
    }
    
    int main() {
      qsort(Database, 5, sizeof Database[0], Compare);
    
      for(int Reg = 0; Reg < 5; Reg++)
        printf("%s\t|%s\t|%s\n", Database[Reg].Col1, Database[Reg].Col2, Database[Reg].Col3);
    }
    I am still at a loss as to why you wanted to do such an ugly thing, but it is possible. I assume that this is mainly to quench your curiosity, not something you plan to use in production code.
    WOW!!! WOW!!! WOW!!!

    It was something like that I really wanted! One way not to declare identifier or typedef!

    Congratulations, thank you! My goal is to learn! And your solution taught me a lot! Helps to understand much more the functioning of the function I mentioned above using an identifier!

    The code is not for a professional program, it's just an example, which I used to better understand the functioning of the compare function, without being limited to what everyone else, and can understand how it works!

    The solution you gave, is quite didactic, showing what the identifier was doing in that position! And now I get it!



    Quote Originally Posted by Schol-R-LEA-2 View Post
    On a side note, I am assuming that this is an example version of the problem, as using opaque names such as col1, col2, etc. would be a bad practice. Perhaps something like this would make more sense:
    It was just an example, I declared Col to be easy for anyone reading!



    Quote Originally Posted by Schol-R-LEA-2 View Post
    As said earlier, though, using a 3-dimensional array would be easier overall:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
     char Database[][3][10] =  { {"carla" , "deia"  , "m"},
                                 {"brenda", "lilia" , "h"},
                                 {"amanda", "karol" , "c"},
                                 {"amanda", "july"  , "b"},
                                 {"brenda", "debora", "a"}};
    
    int Compare(const void *Elemento1, const void *Elemento2) {
      return strcmp(((char *)Elemento1), ((char *)Elemento2));
    }
    
    int main() {
      qsort(Database, 5, sizeof Database[0], Compare);
    
      for(int Reg = 0; Reg < 5; Reg++)
        printf("%s\t|%s\t|%s\n", Database[Reg][0], Database[Reg][1], Database[Reg][2]);
    }
    I know you said you needed it to be a struct, but you never said why.
    I liked this example too, although I do not have to do with the question, help in my learning! Thanks!
    Last edited by marcelo.br; 02-06-2022 at 01:41 PM.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,407
    Quote Originally Posted by marcelo.br
    Congratulations, thank you! My goal is to learn! And your solution taught me a lot! Helps to understand much more the functioning of the function I mentioned above using an identifier!
    You might find it more insightful and more useful for learning, both theoretical and applied, to implement qsort yourself, using a different name but with the same parameter list and return type. Your version of qsort might not end up quite as optimised as the ones in various real world standard library implementations, but figuring out how to do it (given that you already know a few general purpose comparison-based sorting algorithms) would be more instructive than trying to learn through weird self-imposed constraints. It might even help you nail a technical interview for a job some day.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. qsort compare
    By baxy in forum C++ Programming
    Replies: 34
    Last Post: 04-21-2014, 05:37 AM
  2. Using Library qsort() to Compare doubles
    By firetheGlazer in forum C Programming
    Replies: 2
    Last Post: 07-14-2008, 09:29 AM
  3. Compare function for qsort
    By csisz3r in forum C Programming
    Replies: 8
    Last Post: 09-30-2005, 12:45 AM
  4. qsort problems... help!
    By Manseed in forum C Programming
    Replies: 5
    Last Post: 06-21-2003, 04:39 PM
  5. The mount function
    By mattbrrtt in forum Linux Programming
    Replies: 2
    Last Post: 01-13-2003, 08:39 PM

Tags for this Thread