Использование qsort для символьного массива в C
Я пытаюсь использовать qsort
сортировка массива символов. Не понимаю, почему это не работает. У меня есть указатель на функцию сравнения, как man
страницы указывает. Кто-нибудь может сказать мне, что случилось? Спасибо. Мой код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmpfunc( const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
void AlphabetSoup( char str[] ) {
qsort(str, (size_t) strlen(str), (size_t) sizeof(char), cmpfunc);
printf("%sn", str);
}
int main() {
char str1[] = "bcead";
AlphabetSoup(str1);
return 0;
}
выходы: dabce
когда я ожидаю abcde
.
3 ответов
простые ошибки.
использовать char*
вместо int*
на cmpfunc
.
int cmpfunc( const void *a, const void *b) {
return *(char*)a - *(char*)b;
}
при использовании int*
, вместо char*
адрес, на который указывает a
интерпретируется как адрес int
, а не char
.
ваш ввод следующих символов:
+---+---+---+---+---+
| b | c | e | a | d |
+---+---+---+---+---+
в hex, это:
+----+----+----+----+----+
| 62 | 63 | 65 | 61 | 64 |
+----+----+----+----+----+
^ ^
| |
a b
если вы относитесь к адресам, указанным a
и b
as int*
, исходя из int
занимает 4 байта в вашей системе, *(int*)a
может быть
0X62*2^24 + 0X63*2^16 + 0X65*2^8 + 0X61
или
0X62 + 0X63*2^8 + 0X65*2^16 + 0X61*2^24
в зависимости от того, у вас есть большая система с прямым или системе немного прямой.
вы можете аналогично вычислить, что *(int*)b
будет. Как вы можете видеть, вы уже сталкиваетесь с неожиданными сравнениями чисел. К тому времени, когда вы начнете сравнивать значения, которые находятся в других байтовых местоположениях вашего ввода, вы также используете память, которую вы не должны использовать, и вы достигают сфер неопределенного поведения.
у вас есть по крайней мере две проблемы.
во-первых, вы пытаетесь отсортировать содержимое статически определенного литерала, который компилятор может свободно хранить в неизменяемой ОЗУ.
во-вторых, и самое главное, вы бросаете void* в свою функцию сравнения с int*. Предполагая sizeof(int) == 4
и sizeof(char) == 1
, вы эффективно сравниваете символы 0-3 " как целое число "с символами 1-4"как целое число".
в случае sizeof(int) = 8
(т. е. 64 бит компиляторы), тогда вам было бы еще хуже. Бросьте void*
типа char*
и вы должны быть хорошо.
проблема заключается в операторе приведения типа в функции сравнения comfunc
.
int cmpfunc(const void *a, const void *b) {
// error. casting to int * instead of char *
return *(int*)a - *(int*)b;
}
наведение указателя пустоты a
to int *
а затем разыменование означает, что он будет читать sizeof(int)
байт с начала адреса, содержащегося в a
. Таким образом, выражение в операторе return сравнивает sizeof(int)
количество байт от адреса в a
С sizeof(int)
количество байт от адреса в b
, вместо сравнения символов по адресам, содержащимся в указателях a
и b
. Чтобы проиллюстрировать это, я изменил функцию сравнения на
int cmpfunc(const void *a, const void *b) {
printf("comparing %c and %c\n", *((char *)a), *((char *)b));
printf("compare as int %d - %d = %d\n", *(int *)a, *(int *)b, *(int *)a - *(int *)b);
printf("compare as char %d - %d = %d\n", *(char *)a, *(char *)b, *(char *)a - *(char *)b);
return *(char *)a - *(char *)b;
}
и это результат, который я получаю
comparing b and c
compare as int 1634034530 - 1684104547 = -50070017
compare as char 98 - 99 = -1
comparing a and d
compare as int 25697 - 100 = 25597
compare as char 97 - 100 = -3
comparing e and a
compare as int 6578533 - 25697 = 6552836
вы можете видеть разницу в значениях, считываемых при сравнении после типизации в int *
, и после typecasting к char *
. Функция сравнения должна быть изменена на
int cmpfunc(const void *a, const void *b) {
// typecast the void pointers to correct type
return *(char *)a - *(char *)b;
}
кроме того, вам не нужно привести результат и sizeof
оператор, так как они уже возвращают значения типа size_t
. Кроме того, он более удобочитаем и доступен для использования sizeof
на элемент массива. Вы должны просто позвонить qsort
as
qsort(str, strlen(str), sizeof str[0], cmpfunc);