2013-11-22 2 views
-1

Я пытаюсь сортировать массив структур, увеличивая одно поле (двойной номер), но кажется, что qsort() каким-то образом искажает данные в этом массиве (печать массива после вызов показывает, что поля были заполнены некоторыми случайными значениями). Более того, если я изменяю компаратор для сортировки массива в потоке потомков, qsort больше не испортит данные, но он не сортирует массив - после вызова все будет одинаковым. Эта небольшая программа демонстрирует проблему:qsort() Сравнивая массив структур: очень странное поведение

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

/* Macro for comparing floats. */ 
#define CMP_PREC 0.000001 
#define dbl_eq(x, y) (fabs((x) - (y)) < CMP_PREC) 

/* Structure for testing. */ 
struct level { 
    double alt; 
    double volume; 
    double area; 
}; 

/* Create array of levels with random alts. 
* (Other fields are unimportant for this demo). */ 
struct level *create_random_arr(size_t size) { 
    size_t i; 
    struct level *lev = NULL; 
    lev = calloc(sizeof(*lev), size); 
    srand(time(NULL)); 
    for (i = 0; i < size; i++) { 
     lev[i].alt = (double) rand()/1000.0; 
     lev[i].volume = lev[i].area = 0.0; 
    } 
    return lev; 
} 

/* Prints array in format: 
* [index]: alt=[alt], volume=[volume], area=[area]\n */ 
void print_levels(struct level *lev, int lev_cnt) { 
    int i; 
    for (i = 0; i < lev_cnt; i++) { 
     printf("%d: alt=%g, volume=%g, area=%g\n", 
      i, lev[i].alt, lev[i].volume, lev[i].area); 
    } 
} 

/* Comparator for sorting by increasing of alt. */ 
static int levels_compar(const void *a, const void *b) { 
    const struct level *al = (const struct level *) a; 
    const struct level *bl = (const struct level *) b; 
    if dbl_eq(al->alt, bl->alt) { 
     return 0; 
    } else if (al->alt < bl->alt) { 
     return -1; 
    } else { 
     return 1; 
    } 
} 

int main(void) { 
    int size = 10; 
    struct level *lev = NULL; 
    lev = create_random_arr(size); 
    /* Print generated array. */ 
    print_levels(lev, size); 
    /* Sort array by increase. */ 
    qsort(lev, sizeof(*lev), size, levels_compar); 
    /* Print result... very surprising, isn't it? */ 
    printf("----------\n"); 
    print_levels(lev, size); 
    free(lev); 
    return 0; 
} 

ответ

0

Вы переключили параметры в QSort, должно быть:

qsort(lev, size , sizeof(struct level) , levels_compar); 

я заменил *lev с struct level, как я думаю, что это лучше для читаемости кода.

+1

Я не согласен с вашей заменой. 'sizeof * lev' является агностиком к точкам типа lev и устойчив к изменениям в типе. Жесткое кодирование типа приведет к серьезному поломке (сравнимому с тем, что испытывает OP), если тип когда-либо изменяется, и компилятор не может автоматически диагностировать его для вас. –

+0

@R. С помощью структуры вы получаете дополнительный бонус, сразу видя, сколько кода вы просто сломали (по какой области) с помощью ошибок компилятора. И строка find/replace не очень сложна в эти дни. Читаемость> Lazyness – this

+1

Нет, вы этого не делаете. С struct вы не получаете никакой ошибки вообще и ужасного UB во время выполнения. С версией OP (с фиксированным порядком), как я рекомендую, код автоматически работает. –

0
#define dbl_eq(x, y) ((x) - (y) < CMP_PREC) 

Я не думаю, что это правильный метод сравнения. Абсолютные значения следует сравнивать что-то вроде этого пути:

#define dbl_eq(x, y) (fabs((x) - (y)) < CMP_PREC) 
+0

Да, конечно. Просто сделал эту ошибку, когда я готовил код для вопроса. Я сейчас изменю код в вопросе, но проблема все еще существует. –

2

Вы перепутали число-элементов и размера из-элемента аргументов qsort. Таким образом, вокруг него работает:

qsort(lev, size, sizeof(*lev), levels_compar); 

Как уже отмечалось, вы должны также использовать fabs в вашем сравнении макро. Я действительно не думаю, что вам нужно сравнение с допуском для сортировки, однако, потому что вы действительно не ищете равенства, а для восходящего порядка. Я бы просто пошел с == и сделаю последнюю ветвь, потому что она практически никогда не возникает.

+0

Вы не должны сравнивать поплавки с '==' при проверке результатов вычислений для равенства. В этом случае мы не проверяем равенство, а выполняем последовательность. Проверка с допуском может фактически вывести значения немного из последовательности, когда значения должны были быть переключены в массиве, потому что один из них меньше другого, но в пределах допуска, но не переключается, потому что два значения считаются равными. Чтобы сравнить поплавки с допуском, это хороший совет в целом, но я думаю, что здесь ошибочно. (Кроме того, этот совет ведет к допущениям, которые вытягиваются из воздуха). –

+0

Вы правы, равенство можно полностью исключить. – this