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

How to compare C++ string using qsort in c?

本文关键字:qsort 比较 C++ 字符串      更新时间:2023-10-16

我试图学习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

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

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

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

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


如果你有一个简单的类型,你可以使用这个通用的qsorter比较器(当然这是一个糟糕的主意,内联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<T>);


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

最好是面向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使用快速排序。

[更新] 请参阅评论,std::sort 并不总是纯粹的快速排序。

[UPDATE2]

如果你想学习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"));
}

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

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

我建议不要将 qsort 与C++字符串一起使用。

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

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

您的错误在于以 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];

对我有用:

#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;
}