1

I am trying to sort the tuples in the 2D array based on a single column value similar to Javascript's sort function.

arr.sort((a,b) => a[0] - b[0])

C Code :

    #include<stdio.h>
    #include<stdlib.h>
    #include<stdbool.h>

    int comp(const void *a, const void *b){
        return (((int*)a)[0] - ((int *)b)[0]);
    }
    int main(){
        int arr[][4] = {
           {3,2,5,3}, {1,0,5,2},{0,2,2,4},{0,4,4,5}
        };
        int ROWS = sizeof(arr)/sizeof(arr[0]);
        int COLS = sizeof(arr[0])/sizeof(arr[0][0]);
        //qsort(arr, ROWS, COLS*sizeof(int), comp); //this works and sorts arr correctly
    

    int **rect = malloc(ROWS*sizeof(int*));

    for(int i = 0; i < COLS; i++){
        *(rect+i) = malloc(COLS*sizeof(int));
    }
    for(int i = 0; i < ROWS; i++){
        for(int j = 0; j < COLS; j++){
            *(*(rect+i)+j) = arr[i][j];
        }
    }

    qsort(rect, ROWS, COLS*sizeof(int), comp); //this doesn't work on the rect 2d array.
    for(int i = 0; i < ROWS; i++){
        for(int j = 0; j < COLS; j++){
            printf("%d ", *(*(rect+i)+j));
        }
        printf("\n");
    }
}

Quicksort works with the comparator function on the 2D array arr.

When I try using it with the **rect array, the sorting doesn't work. I end up getting a segmentation fault. Can somebody point out where I am going wrong and how **rect is being treated differently from arr?

7
  • 4
    rect is not a multidimensional array but a pointer to a pointer. Pointers are very different from arrays. (((int*)a)[0] accesses a pointer and interpretes it as a int. Commented Mar 25 at 13:04
  • 4
    *(rect+i) = do yourself any everyone else a favor and use array notation: rect[i] = Commented Mar 25 at 13:07
  • 1
    @Bittu970 qsort(rect, ROWS, COLS*sizeof(int), comp); fails as the size is wrong. Should be qsort(rect, ROWS, sizeof rect[0], comp);. But then that is not sorting an array of int. If you want a 2D array, allocate a 2D array and not an array of pointers to arrays. What is the real goal? A 2d array or an array of pointers to arrays? Commented Mar 25 at 13:13
  • @chux I am doing a leetcode question and the input provided (2D array) is of the form pointer to array of pointers. I need to sort this array. That's why I want to understand how to make qsort work with array of pointers to arrays because there are several questions there that provide input in that manner. This is the question : leetcode.com/problems/check-if-grid-can-be-cut-into-sections/…. and the input : bool checkValidCuts(int n, int** rectangles, int rectanglesSize, int* rectanglesColSize) Commented Mar 25 at 13:21
  • Again, a pointer to an array of pointers is not a 2D array. It can be used in many ways like a 2D array, but it is crucially important to understand their differences. Commented Mar 25 at 13:32

2 Answers 2

2

The objects being sorted are of type int * and are being sorted in ascending order of the first int element they point to. See the definition of the function comp2, and the call to qsort in the following, modified code:

#include <stdio.h>
#include <stdlib.h>

int comp2(const void *a, const void *b){
    int *aa = *(int * const *)a;
    int *bb = *(int * const *)b;
    // N.B. this expression is immune from integer overflow, unlike aa[0] - bb[0]
    return (aa[0] > bb[0]) - (aa[0] < bb[0]);
}

int main(){
    static const int arr[][4] = {
       {3,2,5,3}, {1,0,5,2},{0,2,2,4},{0,4,4,5}
    };
    int ROWS = sizeof(arr)/sizeof(arr[0]);
    int COLS = sizeof(arr[0])/sizeof(arr[0][0]);

    int **rect = malloc(ROWS*sizeof(int*));

    // N.B. OP's original code incorrectly used i < COLS here...
    for(int i = 0; i < ROWS; i++){
        rect[i] = malloc(COLS*sizeof(int));
    }
    for(int i = 0; i < ROWS; i++){
        for(int j = 0; j < COLS; j++){
            rect[i][j] = arr[i][j];
        }
    }

    qsort(rect, ROWS, sizeof(rect[0]), comp2);
    for(int i = 0; i < ROWS; i++){
        for(int j = 0; j < COLS; j++){
            printf("%d ", rect[i][j]);
        }
        printf("\n");
    }
}
Sign up to request clarification or add additional context in comments.

8 Comments

Could you explain this line : int *aa = *(int * const *)a; why are there two dereferences in the cast if each object being sorted is of the type int *?
@Bittu970 That only has a single deference. The original type being sorted is int *, so I convert the void * to int ** before dereferencing it. Except the parameter has type const void * (or void const *), so I convert it to int * const * before dereferencing it.
I think I didn't phrase my question correctly. I wanted to ask about the int ** in the cast. I mentioned two dereferences because of the two *'s in the cast. Why was the conversion from void * to int ** necessary?
@Bittu970 Because it is sorting an "array" of int * pointed to by int **rect. A pointer to the element type int * has type int **.
@Bittu970 a * does not always mean a dereferencing operation. It just tells the compiler what type shall be used. Only the one * (...) before the bracket does some dereferencing.
|
2

Use pointers to array

int main()
{

    int arr[][4] = {
        {3,2,5,3}, 
        {1,0,5,2},
        {0,2,2,4},
        {0,4,4,5}};
    size_t ROWS = sizeof(arr)/sizeof(arr[0]);
    size_t COLS = sizeof(arr[0])/sizeof(arr[0][0]);

    /* pointer to array */ // <<----
    int (*rect)[COLS] = malloc(ROWS * sizeof(*rect));

    for(size_t i = 0; i < ROWS; i++)
    {
        for(size_t j = 0; j < COLS; j++)
        {
            rect[i][j] = arr[i][j];
        }
    }

    qsort(rect, ROWS, sizeof(*rect), comp); 
    for(size_t i = 0; i < ROWS; i++)
    {
        for(size_t j = 0; j < COLS; j++)
        {
            printf("%d ", rect[i][j]);
        }
        printf("\n");
    }
    free(rect);
}

5 Comments

Interesting that sizeof(arr)/sizeof(arr[0]) and ROWS * sizeof(*rect) use the size of an object, yet later COLS*sizeof(int) uses the size of a type. Would qsort(rect, ROWS, sizeof rect[0], comp); be more consistent?
@chux nothong interesting in it. Copy paste from OPs code. Good spot anyway
OP's original comp function is also converting the const void * to the wrong type. It should be converting to int (* const)[COLS] but it does not know what COLS is. comp could be defined like this: int comp(const void *a, const void *b){ int (*const aa)[] = (int (*const)[])a; int (*const bb)[] = (int (*const)[])b; return ((*aa)[0] > (*bb)[0]) - ((*aa)[0] < (*bb)[0]); }
… Or rather than "wrong type" I should have wrote "semantically incorrect type". Converting to const int * will work because it's pointing to an int in the original 2-D array. But the intention is to sort arrays of arrays of int, based on the value of the first element of the arrays, not to sort arrays of int, so converting to a pointer to array type is semantically more meaningful, although it would be even more meaningful if we could specify the array dimension and make it a pointer to a complete array type.
… So maybe this covers the "semantic correctness" angle, but with less mess: int comp(const void *a, const void *b){ const int *aa = *(int (*const)[])a; const int *bb = *(int (*const)[])b; return (aa[0] > bb[0]) - (aa[0] < bb[0]); }.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.