I'm using qsort() to sort a 2D array of integers lexiographically, each row being one input, but having problems with pointer arithmetic when traversing a row in the array.
Idea is to concatenate each column in one row into a string, and strcmp
with other strings computed similarly from other rows.
Input is restricted to positive integers only. Some sort examples below,
{1, 1, 10}
before {1, 2, 0}
{3, 1, 5}
before {3, 11, 5}
{1, 19, 2}
before {1, 2, 1}
#define COL_SIZE 3
int cmp_func (const void *a, const void *b) {
char str1[100], str2[100], temp[10];
int i;
const int **ia = (const int **)a;
const int **ib = (const int **)b;
printf("(a: %p), (b: %p)\n", a, b);
str1[0] = '\0';
str2[0] = '\0';
for (i = 0; i < COL_SIZE; i++) {
printf("(ia+%d: %p), (ib+%d: %p)\n", i, (ia + i), i, (ib + i));
sprintf(temp, "%d", (int)*(ia + i));
strcat(str1, temp);
sprintf(temp, "%d", (int)*(ib + i));
strcat(str2, temp);
}
printf("a: %s, b:%s\n", str1, str2);
return(strcmp(str1, str2));
}
int main (void) {
int i, len;
int A[][COL_SIZE] = {{1,2,3},
{1,1,5},
{1,0,1},
{1,2,1},
{1,2,0}};
len = sizeof(A)/sizeof(A[0]);
printf("sizeof(int): %ld, sizeof(int *): %ld\n", sizeof(int), sizeof(int *));
for (i = 0; i < len; i++) {
printf("Address of A[%d][0]: %p\n", i, A[i]);
}
qsort(A, len, COL_SIZE * sizeof(int *), cmp_func);
return 0;
}
Below is the output:
sizeof(int): 4, sizeof(int *): 8
Address of A[0][0]: 0x7fff58e9fb30
Address of A[1][0]: 0x7fff58e9fb3c
Address of A[2][0]: 0x7fff58e9fb48
Address of A[3][0]: 0x7fff58e9fb54
Address of A[4][0]: 0x7fff58e9fb60
(a: 0x7fff58e9fb30), (b: 0x7fff58e9fb48)
(ia+0: 0x7fff58e9fb30), (ib+0: 0x7fff58e9fb48)
(ia+1: 0x7fff58e9fb38), (ib+1: 0x7fff58e9fb50)
(ia+2: 0x7fff58e9fb40), (ib+2: 0x7fff58e9fb58)
a: 131, b:112
(a: 0x7fff58e9fb48), (b: 0x7fff58e9fb60)
(ia+0: 0x7fff58e9fb48), (ib+0: 0x7fff58e9fb60)
(ia+1: 0x7fff58e9fb50), (ib+1: 0x7fff58e9fb68)
(ia+2: 0x7fff58e9fb58), (ib+2: 0x7fff58e9fb70)
Abort trap: 6
The pointer arithmetic for *(ia + 1)
is causing the address in each iteration to jump by 8 (sizeof(int *))
from 0x7fff58e9fb30
to 0x7fff58e9fb38
, whereas the next value in A[0] is stored at 0x7fff58e9fb34
(size of int).
How do I fix this so that I can get an offset of 4 instead of 8?
You're sorting an array of arrays, not an array of pointers. Consequently, you need to clean up the code in the comparison function. Also, the current incarnation of the comparator converts all the numbers to strings before doing any comparisons. It is feasible and sensible to convert one number in each row at a time and stop when you find a difference, only converting the next number when the previous ones are the same.
This leads to the following code, which requires a C99 compiler or a C11 compiler with support for VLAs — variable length arrays — which are mandatory in C99 but optional in C11:
The pointers passed to
cmp_func()
are pointers to arrays ofCOLS
int
s, orint (*)[COLS]
; the assignments toa
andb
generate an arrayint [COLS]
, which decays to anint *
. I usually usev
as a prefix to theconst void *
arguments to a comparator, and remove thev
for the working variables in the function.The test data there includes test values from the commentary, and the output is:
This code is similar to the code by RoadRunner, but primarily differs in the simpler, more efficient comparison function
cmp_func()
which does not necessarily convert all the numbers in each row to strings each time two rows are compared. (In the sample data, the first value is always1
, so at least two pairs of numbers are converted. This is probably not normal, though.)