【问题标题】:How to compare C++ string using qsort in c?如何在c中使用qsort比较C++字符串?
【发布时间】:2012-09-09 16:06:27
【问题描述】:

我尝试学习了c库stdlib的qsort函数。这甚至在c++ 中也提供。但我不明白如何使用它们对c++ 字符串进行排序。我不确定sizeof() 运算符的参数应该是什么,以及我的compare_str 代码是否正确。我试过这段代码:

    #include<iostream>
    #include<cstdlib>
    using namespace std;
    #include<string>

    int compare_str( const void *a, const void *b){
       string  obj = (const char*)a;
       string obj1 = (const char*)b;
       return obj.compare(obj1);
    }
    int main(){
        string obj[4] = {"fine", "ppoq", "tri", "get"};
        qsort(obj, 4, sizeof(obj[0].length()), compare_str);
        for( int i=0; i<4; i++)
            cout<<obj[i]<<endl;
        return 0;
    }

我的输出是:

ppoq
tri
get
fine

我无法找出错误。请帮忙。

【问题讨论】:

  • 我很怀疑这部分“sizeof(obj[0].length())”

标签: c++ string qsort


【解决方案1】:

您不能也不得在std::strings 的数组上使用qsort。元素必须是普通类型,而字符串不是,因此行为未定义。从 25.5/4(“qsort”)开始:

除非base 指向的数组中的对象是普通类型,否则行为是未定义的。

原因是 qsortmemcpy 周围的数组元素,这对于一般的 C++ 对象是不可能的(除非它们足够微不足道)。


如果你确实有一个平凡的类型,你可以使用这个通用的 qsorter-comparator(但当然这是一个糟糕的想法,内联的 std::sort 总是更可取):

template <typename T>
int qsort_comp(void const * pa, void const * pb)
{
    static_assert<std::is_trivial<T>::value, "Can only use qsort with trivial type!");

    T const & a = *static_cast<T const *>(pa);
    T const & b = *static_cast<T const *>(pb);

    if (a < b)  { return -1; }
    if (b < a)  { return +1; }
    return 0;
}

使用: T arr[N]; qsort(arr, N, sizeof *arr, qsort_comp&lt;T&gt;);


不要使用这个。请改用std::sort

【讨论】:

  • 有任何迹象表明这会因字符串而中断?
  • (+1) 是的。我忘了在回答中提到这一点——在这种情况下,这是最重要的。
  • @themel 任何迹象表明它永远不会中断(除了它“应该工作”“适合你”)? 知道某些东西永远不会损坏远好于假设某些东西应该永远不会损坏。
  • 嗯,这真的是“知道它不会破坏,因为你知道这是如何在提问者看到的每个编译器中实现的”与“知道它不会破坏,因为有一个标准说不会”。这两种知识都有其用途,但是当您的程序中断时,指出它违反标准不会使其运行。
  • @themel 我同意忽略琐碎的 UB 和 IB 案例,例如邪恶 reinterpret_casts、联合别名或二进制补码整数,当情况需要并允许时,但我不会做任何事情关于实现像 std::stringqsort 这样抽象的东西的假设(当然不应该复制/破坏任何东西,只能在现有数据周围洗牌,但是很好)。
【解决方案2】:

最好是面向 C++ 并为您的数组使用 std::sort:

#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>

int main() {

   std::string obj[4] = {"fine", "ppoq", "tri", "get"};
   std::sort(obj, obj + 4);
   std::copy(obj, obj + 4, std::ostream_iterator<std::string>(std::cout, "\n"));
}

AFAIK - std::sort 使用快速排序。

[更新] 参见 cmets,std::sort 并不总是纯粹的快速排序。

[更新2]

如果你想学习 qsort - 将std::string 更改为const char* 并根据strcmp 定义函数。请记住,qsort 传递指向数组中元素的指针 - 所以取消引用 const void* 以获取 const char*。见:

#include <stdlib.h>
#include <string.h>

int compare_cstr(const void* c1, const void* c2) 
{ 
   return strcmp(*(const char**)(c1), *(const char**)(c2)); 
}

int main() {

   const char* obj[4] = {"fine", "ppoq", "tri", "get"};
   qsort(obj, 4, sizeof(obj[0]), compare_cstr);
   std::copy(obj, obj + 4, std::ostream_iterator<const char*>(std::cout, "\n"));
}

【讨论】:

  • 在我看过的实现中,std::sort 实际上是一个 introsort(它基本上一个快速排序,除了它跟踪递归深度,如果它去太深使用堆排序代替)。
  • @Jerry 看来我的知识没有你的那么远;)
【解决方案3】:

问题是你给 qsort 一个 C++ 字符串数组。在您的比较函数中,您似乎排除了 C 字符串,因为您将它们转换为 (const char*)。

另外,qsort的第三个参数,数据的大小,其实你给错了值。 sizeof(obj[0].length()) 会导致sizeof(size_t),这显然是错误的。 sizeof(obj[0]) 会更正确,但请记住 qsort 不会调用字符串的复制构造函数,这可能会导致问题。

我建议不要对 C++ 字符串使用 qsort。

请参阅 PiotrNycz 提供的答案以获得正确的解决方案。

【讨论】:

    【解决方案4】:

    您应该使用 C++ 标准库提供的std::sort 模板函数(在&lt;algorithm&gt; 头文件中)。默认情况下,std::sort 使用小于比较运算符对元素进行排序(std::string 已经实现了operator&lt;)。如果您需要指定排序条件(例如,不区分大小写的字符串比较),std::sort 允许您指定排序函数对象。

    例子:

    #include <string>
    #include <algorithm>
    
    bool caseInsensitiveOrdering(const std::string& lhs, const std::string& rhs)
    {
       // return true if lowercase lhs is less than lowercase rhs
    }
    
    int main()
    {
        std::string names[] = {"chuck", "amy", "bob", "donna"};
        size_t nameCount = sizeof(names) / sizeof(names[0]);
    
        // Sort using built-in operator<
        std::sort(names, names + nameCount);
    
        // Sort using comparison function
        std::sort(names, names + nameCount, &caseInsensitiveOrdering);
    }
    

    【讨论】:

      【解决方案5】:

      您的错误在于qsort 中的大小声明。预期的是成员的大小,在您的情况下,它是一个字符串。所以你想使用:

      qsort(obj, 4, sizeof(string), compare_str);
      

      但是,您需要使用指向字符串的指针,而不是字符串本身。然后,代码应如下所示:

      int compare_str( const void *a, const void *b){
         const string*  obj = (const string*)a;
         const string* obj1 = (const string*)b;
         return obj->compare(*obj1);
      }
      
      // ...
      
      string* obj[4] = { new string("fine"), new string("ppoq"),
                         new string("tri"), new string("get") };
      qsort(obj, 4, sizeof(string*), compare_str);
      // And delete the objects
      for(int i = 0 ; i < 4 ; ++i) delete obj[i];
      

      【讨论】:

      • 什么...?好的,至少现在代码是正确的,至少如果您将objobj1 更改为const std::string**s。
      【解决方案6】:

      为我工作:

      #include<iostream>
      #include<cstdlib>
      using namespace std;
      #include<string>
      
      int compare_str( const void *a, const void *b){
         string* obj = (string*)a;
         string* obj1 = (string*)b;
         return obj->compare(*obj1);
      }
      int main(){
          string obj[4] = {"fine", "ppoq", "tri", "get"};
          qsort(obj, 4, sizeof(string), compare_str);
          for( int i=0; i<4; i++)
              cout<<obj[i]<<endl;
          return 0;
      }
      

      【讨论】:

      • 确实:未定义的行为通常看起来“有效”。它会继续工作,直到你为最重要的客户做那个关键的演示,然后它就会落空。
      • -1 - "works for me" 在 C++ 中从来都不是正确性的证明,尤其是当 UB 在这种情况下如此明显时(替代方案是 在各个方面都优于)。
      猜你喜欢
      • 1970-01-01
      • 2019-06-19
      • 2019-07-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多