如何在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::string
s 的数组上使用qsort
。元素必须是普通类型,而字符串不是,因此行为未定义。从 25.5/4(“qsort”)开始:
除非
base
指向的数组中的对象是普通类型,否则行为是未定义的。
原因是 qsort
将 memcpy
周围的数组元素,这对于一般的 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<T>);
不要使用这个。请改用std::sort
。
【讨论】:
有任何迹象表明这会因字符串而中断? (+1) 是的。我忘了在回答中提到这一点——在这种情况下,这是最重要的。 @themel 任何迹象表明它永远不会中断(除了它“应该工作” 或“适合你”)? 知道某些东西永远不会损坏远好于假设某些东西应该永远不会损坏。 嗯,这真的是“知道它不会破坏,因为你知道这是如何在提问者看到的每个编译器中实现的”与“知道它不会破坏,因为有一个标准说不会”。这两种知识都有其用途,但是当您的程序中断时,指出它违反标准不会使其运行。 @themel 我同意忽略琐碎的 UB 和 IB 案例,例如邪恶reinterpret_cast
s、联合别名或二进制补码整数,当情况需要并允许时,但我不会做任何事情关于实现像 std::string
或 qsort
这样抽象的东西的假设(当然不应该复制/破坏任何东西,只能在现有数据周围洗牌,但是很好)。【参考方案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
模板函数(在<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);
【讨论】:
【参考方案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];
【讨论】:
什么...?好的,至少现在代码是正确的,至少如果您将obj
和obj1
更改为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++字符串?的主要内容,如果未能解决你的问题,请参考以下文章