Использование 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);