如何在c中使用qsort比较C++字符串?

Posted

技术标签:

【中文标题】如何在c中使用qsort比较C++字符串?【英文标题】:How to compare C++ string using qsort in 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())” 【参考方案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 在这种情况下如此明显时(替代方案是 在各个方面都优于)。

以上是关于如何在c中使用qsort比较C++字符串?的主要内容,如果未能解决你的问题,请参考以下文章

C/C++sort/qsort使用详解

qsort()/bsearch() 的外部“C”和“C++”版本的重载解决方案

如何在C中对指向char的指针数组进行qsort?

如何在 C++ 中比较两个结构字符串

qsort()函数(C)

字符串数组排序(C语言)(qsort库函数)