Search code examples
carrayssortingmultidimensional-arrayqsort

Sorting a 2 dimensional array of strings by a specific field


Given a two dimensional array decalred below:

char * Arr[4] = 
{
    {"124 -346  DATA...."},
    {"39479 -32 MOREDATA...."},
    {"12 -1 DATA2...."},
    {"100 -45 DATA4...."}
};

i'm trying to use qsort() to sort this function according to the SECOND field, meaning the strings would be ordered according to the lowest second value(-1,-32,-45,-346). I know how to make this function if each value were only one digit, but the digits in the program could be arbitrarily long. here is what i have but the program crashes, if there is a more efficient way to sort this data i would love to here it(i know my method can't be very efficient).

Sort function(which qsort() calls):

inline void GetStr(char *ix, char* Result)  //call to get second number in function
{
    char *spacing;              //iterator to traverse
    spacing = ix;               //iterator = pos of ix
    int LEN = 0;                //length and offset
    int Offset = 0;

    while(*spacing != ' ')      //while not at end of first num
    {
        Offset++;               //offset is more
        spacing++;
    }
    spacing++;                  //go one ahead of the space
    Offset++;

    while(*spacing != ' ')      //while not end of second number
    {
        spacing++;
        Offset++;
        LEN++;                  //length of number
    }
    strncpy(Result, ix + (Offset - LEN),LEN);
}

int sort(const void* a, const void* b)
{
    char *ia = *(char**)a;
    char *ib = *(char**)b;

    char * Str;
    char * Str2;
    GetStr(ia, Str);                                    //getting some strange errors....... program just crashes
    GetStr(ib, Str2);
    printf("Str: %s Str2: %s", Str, Str2);
    int n1 = atoi(Str);
    int n2 = atoi(Str2);
    return (n1 > n2);
}

Solution

  • I believe you have at least one problem here:

    strncpy(Result, ix + (Offset - LEN),LEN);
    

    If you look at the documentation for strncpy, you will see that it does not automatically null-terminate the copied string if you hit the character limit. Therefore your Result strings are not null-terminated.

    Try changing to this:

    strncpy(Result, ix + (Offset - LEN),LEN);
    Result[LEN] = '\0';
    

    Of course, you still need to provide memory for the Result string. Currently you are passing an uninitialized pointer into GetStr():

    char * Str;
    char * Str2;
    

    Since these are fairly small integers you can use statically allocated storage like this:

    #define MAX_RESULT_LEN 64
    
    /* ... */
    
    char Str[MAX_RESULT_LEN]
    char Str2[MAX_RESULT_LEN]
    
    /* ... */
    
    if (LEN > MAX_RESULT_LEN - 1) {
        LEN = MAX_RESULT_LEN - 1;
    }
    
    strncpy(Result, ix + (Offset - LEN),LEN);
    Result[LEN] = '\0';
    

    Finally, there are some issues with your sort() function. If you look at the qsort() documentaton, you can see that the return value should be "an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second". The easiest way to achieve this is with the logic n1 - n2 instead of the n1 < n2.

    I also thought you're sort arguments of type char ** were odd as well, but upon further reflection I realize they are correct. From the qsort docs: "two arguments that point to the objects being compared". So indeed they will be pointers to C strings or char **.

    So here is the final version:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_RESULT_LEN 64
    
    void GetStr(char *ix, char* Result)  //call to get second number in function
    {
        char *spacing;              //iterator to traverse
        spacing = ix;               //iterator = pos of ix
        int LEN = 0;                //length and offset
        int Offset = 0;
    
        while(*spacing != ' ')      //while not at end of first num
        {
            Offset++;               //offset is more
            spacing++;
        }
        spacing++;                  //go one ahead of the space
        Offset++;
    
        while(*spacing != ' ')      //while not end of second number
        {
            spacing++;
            Offset++;
            LEN++;                  //length of number
        }
    
        if (LEN > MAX_RESULT_LEN - 1) {
            LEN = MAX_RESULT_LEN - 1;
        }
    
        strncpy(Result, ix + (Offset - LEN),LEN);
        Result[LEN] = '\0';
    }
    
    int sort(const void* a, const void* b)
    {
        char *ia = *(char **)a;
        char *ib = *(char **)b;
    
        char Str[MAX_RESULT_LEN];
        char Str2[MAX_RESULT_LEN];
    
        GetStr(ia, Str);
        GetStr(ib, Str2);
    
        printf("Str: %s Str2: %s", Str, Str2);
        int n1 = atoi(Str);
        int n2 = atoi(Str2);
        return (n1 - n2);
    }
    
    int main(void) {
        char * Arr[4] = 
        {
            {"124 -346  DATA...."},
            {"39479 -32 MOREDATA...."},
            {"12 -1 DATA2...."},
            {"100 -45 DATA4...."}
        };
    
        qsort(Arr, 4, sizeof(char *), sort);
    }