5

First, I defined a dynamic array with 2 columns and 10 row. The integer number is set to 10 here just for example.

int** array;
int number = 10;

array = malloc(number * sizeof(int*));

for (i = 0; i < number; i++)
    array[i] = malloc(2 * sizeof(int));

Then I try to use qsort() on it.

qsort( array, number, sizeof array[0], compare );

This is my compare function. It sorts by the integer values in the first column, then sorts by the second column while preserving the order in the first column. E.g. "0 2, 1 7, 0 1" will become "0 1, 0 2, 1 7".

int compare ( const void *pa, const void *pb ) {
    int (*a)[1] = pa;
    int (*b)[1] = pb;
    if ( (a[0][0] < b[0][0]) || (a[0][0] == b[0][0])&&(a[1][0] < b[1][0]) ) return -1;
    if ( (a[0][0] > b[0][0]) || (a[0][0] == b[0][0])&&(a[1][0] > b[1][0]) ) return +1;
    return 0;
}

Question

This worked with a static array. I know it doesn't work now because I have a dynamic array, which is an array of pointers.

How can I adapt this code to work with the dynamically created multi-dimensional array?

Legendre
  • 3,108
  • 7
  • 31
  • 46

6 Answers6

15

sample code

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

int compare ( const void *pa, const void *pb ) {
    const int *a = *(const int **)pa;
    const int *b = *(const int **)pb;
    if(a[0] == b[0])
        return a[1] - b[1];
    else
        return a[0] - b[0];
}
/*
#define NUMCMP(x,y) (((x) < (y)) ? -1 : ((x) > (y)) ? 1 : 0)

int compare ( const void *pa, const void *pb ) {
    const int (*a)[2] = *(const int (**)[2])pa;
    const int (*b)[2] = *(const int (**)[2])pb;
    int tmp;
    if((tmp=NUMCMP((*a)[0], (*b)[0]))==0)
        return NUMCMP((*a)[1], (*b)[1]);
    else
        return tmp;
}
*/    
int main(void){
    int **array;
    int number = 10;
    int i;

    array = malloc(number * sizeof(int*));
    for (i = 0; i < number; i++){
        array[i] = malloc(2 * sizeof(int));
        array[i][0] = rand()%20;
        array[i][1] = rand()%20;
    }
    for(i = 0;i < number;++i)
        printf("%2d, %2d\n", array[i][0], array[i][1]);

    printf("\n");

    qsort(array, number, sizeof array[0], compare);

    for(i = 0;i < number;++i)
        printf("%2d, %2d\n", array[i][0], array[i][1]);

    return 0;
}

what *(const int **)pa

array = {(int *), (int *), (int *), ... , (int *) }

qsort need each element address for element (for swap, etc. Because the size and number and start address of the element since only the given information).

E.g &(int *), &(int *)

so (int **) pass to function compare.

call compare(int **, int **) &(int*) meant at arg int**

compare function prototypeis cmp(const void*, const void*)

cast (const int**)pa is cast to passed original pointer.

*((const int **)pa) is dereference original element pointer(int*)

BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
  • 3
    Note that `return a[1] - b[1];` (and similar expressions) is dangerous if the value in `a` is large and positive and the value in `b` is large and negative (or vice versa). You can get arithmetic overflow, which leads to undefined behaviour. For most reasonable data sets, this will not be a problem. However, you should be aware of the issue. – Jonathan Leffler Jun 19 '13 at 23:29
  • @JonathanLeffler goot point, thanks. but note that in this case 0 or more value each is value of `rand`. – BLUEPIXY Jun 19 '13 at 23:37
  • I am interested in second solution whre you did: `const int (*a)[2] = *(const int (**)[2])pa; const int (*b)[2] = *(const int (**)[2])pb;` which is quite confusing. Could you explain it that how this is valid because AFAIK, `string` is of type `int **` here and hence `pa` and `pb` should be of type `int **`. Am I missing something? – haccks Jun 22 '14 at 22:14
  • @haccks, hint : `array[i] = malloc(2 * sizeof(int));` – BLUEPIXY Jun 23 '14 at 09:51
  • @BLUEPIXY; Yes. I understand what you are saying, but here `array` is of `int **` type and hence `array[i]` is of `int *`. Allocating memory to `array[i] = malloc(2 * sizeof(int));` doesn't make it `int (*)[2]` type, it is still `int *` type. – haccks Jun 23 '14 at 09:55
  • @haccks , [small sample code](http://ideone.com/Zd9st3) : In a sample, receives a pointer to the same address as `a`:`int*` (or `&a`:`int(*)[2]`) in the comparison function. It can be interpreted by either casting it. – BLUEPIXY Jun 23 '14 at 10:08
  • OK. You mean here we are seeing the address of elements instead of *pointer type* ? – haccks Jun 23 '14 at 10:13
  • @haccks , I do not know well the meaning of the your question, After all, It only has been cast in the pointer you want to interpret it is received by the `void *` a pointer to the address of memory of size of the 2 `int`. – BLUEPIXY Jun 23 '14 at 10:20
  • OK. One question at one time :). Tell me the type of `array` ? – haccks Jun 23 '14 at 10:34
  • @haccks `int **array;` – BLUEPIXY Jun 23 '14 at 10:36
  • OK. If I will pass it to `qsort` then what would be the type of `p` and `q` in `compare` function? – haccks Jun 23 '14 at 10:40
  • 1
    @haccks , E.g `int *p1 = malloc(2*sizeof(int)); int (*p2)[2] = malloc(2*sizeof(int));` --> `func((void*)&p1)` <-> `func((void*)&p2)` : Are both the same as a pointer to another type for `func`. It can be interpreted either way. – BLUEPIXY Jun 23 '14 at 10:43
  • No. I am asking about type of pointer passed to `compare` function when `int **` type is passed to `qsort`. By the way do you mean `func((void*)&p2)` ----> `func((void*)p2)` ? (I know because of your poor English you are finding it hard to explain but I hope you will do :) ) – haccks Jun 23 '14 at 10:51
  • @haccks , `int compare ( const void *pa, const void *pb ) ;` be treated in the qsort function Remember that it is a pointer to an object. – BLUEPIXY Jun 23 '14 at 10:52
  • I read that, **base pointer in `qsort` must point to the first element in the array and `pa, pb` should be the pointers to array elements**. That means, pointer passed to `pa` and `pb` is of type `int **` because `array` is of type `int **`, am I wrong? – haccks Jun 23 '14 at 11:00
  • 1
    @haccks , It will receive as a `void *` instead of `int **` The comparison function. type of `pb` and `pa` is only `void *` instead of `int **`. – BLUEPIXY Jun 23 '14 at 11:00
  • Oh! Good. That mean you only need to cast `pa` and `pb` to pointer to elements of array, right? – haccks Jun 23 '14 at 11:06
  • @haccks yes, There is no difference are you trying to operate after all only an address of 2 `int`. – BLUEPIXY Jun 23 '14 at 11:08
  • Yes. Finally we need to operate on array of `int[2]`. So it can be done in either ways: `const int *a = *(const int **)pa; const int *b = *(const int **)pb;` or `const int (*a)[2] = *(const int (**)[2])pa; const int (*b)[2] = *(const int (**)[2])pb;`. Got it!. But is this also valid: `const int (*a)[2] = (const int (*)[2])pa; const int (*b)[2] = (const int (*)[2])pb;` ? – haccks Jun 23 '14 at 11:15
  • @haccks , _valid?_ : no, because call like this `func((void*)&p2)`, it is `int (**)[2]`. – BLUEPIXY Jun 23 '14 at 11:19
  • You mean `pa, pb` can't be casted to `(const int (*)[2])` in this case ? – haccks Jun 23 '14 at 11:28
  • Why? I do not understand what you said in previous comment : *because call like this `func((void*)&p2)`, it is `int (**)[2]`*...... What is `func((void*)&p2)` here? – haccks Jun 23 '14 at 11:32
  • @haccks `p2` type is `int (*)[2]`. pointer to `p2` (`&p2`) type is `int (**)[2]`. – BLUEPIXY Jun 23 '14 at 11:33
  • Yes . I know. Did you mean by `func((void*)&p2)` function call of `compare` in `qsort` ? – haccks Jun 23 '14 at 11:35
  • @haccks This is the same as the meaning. – BLUEPIXY Jun 23 '14 at 11:35
  • @haccks Why ask a question like that at this late hour? Why do you understand and accept `int(*)[2]` if that even though it has already been understood to be received by the `int **` when `int *` is operated as an element is received is `int(*)[2]`(Pointer is as it is this.) Are you ? – BLUEPIXY Jun 23 '14 at 11:41
  • @BLUEPIXY; Because currently I am working on `qsort` that's why I am asking it to you :) (sorry for the inconvenience). – haccks Jun 23 '14 at 11:44
  • you are welcome. It works in the array with the elements `int *`. – BLUEPIXY Jun 23 '14 at 11:45
  • say again, "pointer to an object." , case of `int*` --> `int**`. case of `int (*)[2]` to `int (**)[2]`. – BLUEPIXY Jun 23 '14 at 11:56
  • Well I am confused because I do not understand how both of `const int *a = *(const int **)pa;` and `const int (*a)[2] = *(const int (**)[2])pa;` works. I think I do not actually understand how `qsort` call `compare` function. Thanks for your effort. I am awarding you a bounty of worth 100 reputation for your help and effort. :) – haccks Jun 23 '14 at 12:22
  • I think good to try and implement the qsort for understanding. – BLUEPIXY Jun 23 '14 at 12:29
  • I implemented in lots of code but still wondering [how `compare` function worked in some cases](http://ideone.com/fA722p). – haccks Jun 23 '14 at 12:34
  • @haccks Do you want to hear about it (the part that is commented out) ? – BLUEPIXY Jun 23 '14 at 12:50
  • 1) `return strcmp(pp, qq);` in `comp_string` : this is wrong. – BLUEPIXY Jun 23 '14 at 13:29
  • 2)`return strcmp(pp, qq);` in `comp_string_array_2D` : this is OK. – BLUEPIXY Jun 23 '14 at 13:30
  • But `return strcmp(pp, qq);` is working in `comp_string` as well. – haccks Jun 23 '14 at 13:34
  • no , you miss read. case 1) pointer to pointer to address of memory of array of char. strcmp need address of memory, not pointer (address). E.g P -> "string", strcmp need "string", not p(void* is &p). – BLUEPIXY Jun 23 '14 at 13:38
  • Case 1) is wrong for both of `ptr_to_ptr` and `array_of_ptr` function? – haccks Jun 23 '14 at 13:51
  • case 2) `void*` is "string", not `&P`. So strcmp can be applied as it . – BLUEPIXY Jun 23 '14 at 14:03
  • But I think the function call made by `qsort` is something like `comp_string_array_2D((void *)(*)[10], (void*)(*)[10])` so `p` and `q` are address of "strings" instead of "string". – haccks Jun 23 '14 at 14:09
  • remember small sample code. real 2D array layout [[1st 10][2nd 10][3rd 10]]. pointer to treat in qsort is a pointer to the elements of each.(E.g. [1st 10]) It is a string(char [10]) in this case. – BLUEPIXY Jun 23 '14 at 14:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56121/discussion-between-haccks-and-bluepixy). – haccks Jun 23 '14 at 14:22
  • Could you tell me please the reason for the warning in this [question](http://stackoverflow.com/q/18947438/2455888). I tried it using casting and it worked. `const int (*a)[2] = (int (*)[2])pa;` – haccks Jun 24 '14 at 14:25
  • @haccks , case of `const int (*a)[2] = pa;` : I think to be a possibly excessive warning. case of `const int (*a)[2] = (int (*)[2])pa;` : Warning in this case is reasonable. because It is gone in the `const`. – BLUEPIXY Jun 24 '14 at 14:43
  • Sorry, I mean `const int (*a)[2] = (const int (*)[2])pa;` worked. – haccks Jun 24 '14 at 14:47
  • @haccks I think that it's become such warnings when trying to assign the result has been translated to a `(void *)` automatically once perhaps the case of gcc. It does not occur with clang. – BLUEPIXY Jun 24 '14 at 14:54
0

Since you now have an array of pointers, the arguments to your comparison function are going to be pointers to pointers. Use them like this:

int *const *a = pa;
int *const *b = pb;

Now you have a and b as two pointers into the array you're sorting. Each one points to a single element that the sort function is asking you to examine. You can access these elements as *a and *b or a[0] and b[0] but should not ever use a[1] or b[1]. If the sort function asks you to compare the first element in the array (*a) and the fifth element in the array (*b), a[1] and b[1] are the second and sixth elements of the array - completely irrelevant to the comparison you're supposed to be doing.

After the first level of dereferencing, you're allowed to do whatever you need to do to examine the elements being compared. Since your array elements are themselves pointers to arrays (of 2 int each), the ints can be accessed as a[0][0] a[0][1] b[0][0] b[0][1]. Notice this is the opposite order from your a[1][0] and b[1][0].

Writing them as (*a)[0] would provide a reminder that the first level of indirection is a "single-element-access-only" pointer. I'm undecided on whether this makes the whole thing clearer.

0

I came across this thread in search of a ditto problem of mine, and I lastly end-up doing the below thing.

static int compareMatrixElements(const void *p1, const void *p2)
{
    return ((*(int const *) p1) - (*(int const *) p2));
}

void sortMatrix(int** matrix, int r, int c)
{
    int sort_matrix[r][c];

    for(int i = 0; i < r; i++) {

        memcpy(sort_matrix[i], matrix[i], c * sizeof(int));
    }

    qsort(sort_matrix, r*c, sizeof(int), compareMatrixElements);

    for(int i = 0; i < r; i++) {

        memcpy(matrix[i], sort_matrix[i], c * sizeof(int));
    }
}

In the above code of mine I used qsort API directly, one can use this api or any other sort algo on a contiguous memory, but what If you are having a matrix whose rows are pointer to the memory of its columns (like in my case as well as described in the question of this thread). Hence I copied such matrix into a contiguous memory, ran sort on that contiguous memory and copied back the sorted elements to the matrix.

The above solution worked for me, I thought it might be helpful others so I posted it, suggest any improvement for the same.

Sunny Shukla
  • 537
  • 1
  • 6
  • 17
0
const int *a = *(const int **)pa;
const int *b = *(const int **)pb;

@BLUEPIXY, this is not correct. Just enough

const int *a = pa;
const int *b = pb;

becouse pa is const void * (array[0]) and it very well cast to const int *

0
const int (*a)[2] = *(const int (**)[2])pa;
const int (*b)[2] = *(const int (**)[2])pb;

@BLUEPIXY, this is not correct. Just enough

const int (*a)[2] = (int(*)[])pa;
const int (*b)[2] = (int(*)[])pb;
-1

sizeof array[0] will be "2 * sizeof(int)", for this static array.

int array[10][2];

sizeof array[0] will be "sizeof(int*)", for this pointer-to-pointer. sizeof array[0][0] will be "sizeof(int)", for this pointer-to-pointer.

int **array;

So, First thing is you cannot use "qsort( array, number, sizeof array[0], compare );" in case of pointer-to-pointer implementation.

sukumarst
  • 265
  • 1
  • 5