【问题标题】:C qsort not working correctlyC qsort 无法正常工作
【发布时间】:2011-08-31 12:15:51
【问题描述】:

我不知道我做错了什么,但是下面的代码没有正确地对数组进行排序。

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

int compare(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}

int main()
{
    int x[] = { -919238029,
            -889150029,
            -826670576,
            -579609061,
            -569653113,
            -305140505,
            -216823425,
            -193439331,
            -167683147,
            -49487019,
            -45223520,
            271789961,
            275570429,
            444855014,
            559132135,
            612312607,
            664554739,
            677860351,
            1005278191,
            1031629361,
            1089012280,
            1115952521,
            1521112993,
            1530518916,
            1907515865,
            1931470931,
            -1631034645,
            -1593702794,
            -1465300620,
            -1263094822
         };
    int i;

    qsort(x, 30, sizeof(int), compare);
    for(i = 0; i < 30; i ++)
        printf("%d\n", x[i]);

    return 0;
}

产生以下输出:

1521112993
1530518916
1907515865
1931470931
-1631034645
-1593702794
-1465300620
-1263094822
-919238029
-889150029
-826670576
-579609061
-569653113
-305140505
-216823425
-193439331
-167683147
-49487019
-45223520
271789961
275570429
444855014
559132135
612312607
664554739
677860351
1005278191
1031629361
1089012280
1115952521

我的意思是,问题/必须/在我的比较功能中。有人注意到什么奇怪的吗?

【问题讨论】:

    标签: c qsort


    【解决方案1】:

    是的,你的“比较”溢出了。 :(

    原因:

    当你从一个正数中减去一个负数时,你的结果不一定是正数;如果它不能用数据类型表示,它会“环绕”另一边。

    示例:

    如果你的整数只能从 -8 到 7(4 位),那么当你比较 4 和 -4 时会发生什么?
    好吧,你得到 8,即二进制的1000,即-8。所以 4 小于 -4。

    道德:

    不要做减法而不是比较,即使他们在学校告诉你“看看这有多酷”!

    【讨论】:

      【解决方案2】:

      一般情况下,您不能使用减法来比较整数。或者,更准确地说,您可以,但仅在您确定减法不会溢出的情况下。在你的情况下减法溢出,产生完全没有意义的结果(甚至没有提到当有符号整数减法溢出时行为是未定义的)。

      在值ab 之间生成三态C 样式比较的常用习惯用法是(a &gt; b) - (a &lt; b) 表达式。它适用于几乎任何可比类型的数据。在您的情况下,比较函数可能如下所示

      int compare(const void* a, const void* b)
      {
        int va = *(const int*) a;
        int vb = *(const int*) b;
        return (va > vb) - (va < vb);
      }
      

      【讨论】:

      • 不过,不要在其他语言中尝试使用布尔值。 ;)
      • @Mehrdad:如果你指的是 C++,那你就错了。这个成语在 C++ 中也能很好地工作。在 C++ 中,bool 类型的值在减法之前提升为 int。同样,这个习惯用法对于 C 和 C++ 中的所有基本类型都是完全安全的。
      • @AndreyT:我当然不是指的是 C++(主要是 C# 和 Java)。 ;) C++ 保留了很多相同的行为,所以它显然很好。
      • 常见于哪些人?我更喜欢直截了当的va &lt; vb? -1 : va &gt; vb? 1 : 0,如果您有多个排序字段,它易于理解、易于反转并且易于扩展。而且 Mehrdad 的评论是准确的——在许多语言中,布尔值不会隐式转换为整数(当然,这 一个 C 问题,但是,他 确实 咧嘴笑了)。
      • @Jim Balter:我发现(va &gt; vb) - (va &lt; vb) 明显更直接。起初它看起来有点新奇,但它的对称性质很快使它比?: 运算符的两级卷积具有不明显的分组更易读。
      【解决方案3】:

      为了补充 Mehrad 的正确答案,这里有一种使用 SortChecker 自动查找代码中错误的方法:

      $ LD_PRELOAD=$HOME/sortcheck-master/bin/libsortcheck.so ./a.out
      a.out[38699]: qsort: comparison function is not transitive (comparison function 0x40057d (/home/iuriig/a.out+0x40057d), called from 0x400693 (/home/iuriig/a.out+0x400693), cmdline is "./a.out")
      -919238029
      -889150029
      ...
      

      此警告表示 compare 报告 x &lt; y, y &lt; z 而不是 x &lt; z 用于某些输入。要进一步调试此问题,请运行

      export SORTCHECK_OPTIONS=raise=1
      

      并检查生成的编码转储。

      【讨论】:

        【解决方案4】:

        我正在使用上面的信息给出一个代码示例。在我的编译器和系统中,我得到的结果与提出问题的 Ram 相同。这表明我的整数类似于 Ram 的整数。我按照 Mehrdad 的建议修改了我的代码,以使用比较运算符而不是减法。然后我得到了正确排序的数字。

        代码如下:

            #include <stdio.h>
            #include <stdlib.h>
        
            int compare(const void* a, const void* b)
            {
                int
                    n1 = * (int *) a,
                    n2 = * (int *) b;
        
                /*
                Usine the ternary to express along the lines of
                    if
                    elseif
                    elseif
                    .
                    .
                    .
                    else
                */
        
                return 
                    n1 > n2             // if
                    ? 1                 // then
                    : n1 == n2          // else if
                    ? 0                 // then
                    : -1                // else
                    ;                   // end if
            }
        
            int main(int argc, char * argv[])
            {
                int x[] = 
                { 
                    -919238029, -889150029, -826670576, -579609061, -569653113, -305140505, -216823425, -193439331,
                    -167683147, -49487019,  -45223520,  271789961,  275570429,  444855014,  559132135,  612312607,
                    664554739,  677860351,  1005278191, 1031629361, 1089012280, 1115952521, 1521112993, 1530518916,
                    1907515865, 1931470931, -1631034645,-1593702794,-1465300620,-1263094822
                };
        
                int 
                    i = 0,                          // index
                    imax = sizeof(x)/sizeof(int);   // max value for index
        
                FILE * outf = 0;
        
                if ( !(outf = fopen("output.txt", "wt")) )
                {
                    puts("outf == 0 which is an error trying to open \"output.txt\" for writing.\n");
                    getch();
                    return;
                }
        
                qsort(x, imax, sizeof(int), compare);
        
        
                for(i = 0; i < imax; i ++)
                    fprintf(outf, "%d\n", x[i]);
        
                fclose(outf);
        
                return 0;
            }
        

        我得到这个输出:

        -1631034645
        -1593702794
        -1465300620
        -1263094822
        -919238029
        -889150029
        -826670576
        -579609061
        -569653113
        -305140505
        -216823425
        -193439331
        -167683147
        -49487019
        -45223520
        271789961
        275570429
        444855014
        559132135
        612312607
        664554739
        677860351
        1005278191
        1031629361
        1089012280
        1115952521
        1521112993
        1530518916
        1907515865
        1931470931
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-08-01
          • 2013-08-26
          • 2010-11-29
          • 2015-02-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多