【问题标题】:memcmp sortmemcmp 排序
【发布时间】:2010-10-09 15:27:09
【问题描述】:

我有一个缓冲区和几个指向它的指针。我想根据指针指向的缓冲区中的字节对指针进行排序。

qsort() 和 stl::sort() 可以被赋予自定义比较函数。例如,如果缓冲区是零终止的,我可以使用 strcmp:

int my_strcmp(const void* a,const void* b) {
  const char* const one = *(const char**)a,
  const two = *(const char**)b;
  return ::strcmp(one,two);
}

但是,如果缓冲区不是以零结尾的,我必须使用需要长度参数的 memcmp()。

有没有一种简洁有效的方法可以在没有全局变量的情况下将缓冲区的长度放入我的比较函数中?

【问题讨论】:

    标签: c++ c performance sorting stl


    【解决方案1】:

    通过 std::sort,您可以像这样使用 Functor:

    struct CompString {
        CompString(int len) : m_Len(len) {}
        bool operator<(const char *a, const char *b) const {
            return std::memcmp(a, b, m_Len);
        }
    private:
        int m_Len;
    };
    

    那么你可以这样做:

    std::sort(begin(), end(), CompString(4)); // all strings are 4 chars long
    

    编辑:来自评论建议(我猜这两个字符串都在一个公共缓冲区中?):

    struct CompString {
        CompString (const unsigned char* e) : end(e) {}
        bool operator()(const unsigned char *a, const unsigned char *b) const {
            return std::memcmp(a, b, std::min(end - a, end - b)) < 0;
        }
    private:
        const unsigned char* const end;
    };
    

    【讨论】:

    • struct CompString { CompString (const unsigned char* e) : end(e) {} bool operator()(const unsigned char a, const unsigned char *b) const { return (- 1 == ::memcmp(a, b, __min(end-a,end-b))); } 私有的:const unsigned char const end; };
    • 这种方法让我开始了。我刚刚发布了一些小的更正。请更新代码。
    • 我无法让操作符
    • 为了完整起见,我应该说我的解决方案未能考虑字节何时匹配 - 直到缓冲区的末尾,但显然一个字符串会比另一个字符串长.. .
    • 你在原始请求中暗示所有被比较的字符串长度相同......
    【解决方案2】:

    使用 C 函数 qsort(),不,不使用全局变量就无法将长度传递给您的比较函数,这意味着它不能以线程安全的方式完成。一些系统有一个qsort_r() 函数(r 代表可重入),它允许您传递一个额外的 context 参数,然后将其传递给您的比较函数:

    int my_comparison_func(void *context, const void *a, const void *b)
    {
        return memcmp(*(const void **)a, *(const void **)b, (size_t)context);
    }
    
    qsort_r(data, n, sizeof(void*), (void*)number_of_bytes_to_compare, &my_comparison_func);
    

    【讨论】:

    • BSD 系统,包括 MacOS X,包括 qsort_r();其他平台似乎没有。
    【解决方案3】:

    您是否有不能对缓冲区进行空终止的原因?

    如果没有,由于您使用的是 C++,您可以编写自己的函数对象:

     struct MyStrCmp {
        MyStrCmp (int n): length(n) { }
        inline bool operator< (char *lhs, char *rhs) {
           return ::strcmp (lhs, rhs, length);
        }
        int length;
     };
     // ...
     std::sort (myList.begin (), myList.end (), MyStrCmp (STR_LENGTH));
    

    【讨论】:

    • 注意:std::sort 采用迭代器而不是容器。
    • 哈哈! -- 至少你得到了正确的 std::sort 调用......忘记了迭代器;)
    • 谢谢,您的回答已被接受,因为您的回答比 Evan 略快,而且 Evan 已经获得了 分。我放了 Evan 的 cmets 的工作版本;请更新您的代码?
    • @Will: 噢,代表偏见 :(。哦,好吧,没有难过的感觉,很高兴你能找到一个可行的解决方案。
    【解决方案4】:

    您能否将缓冲区指针 + 长度打包到一个结构中并将该结构的指针作为void * 传递?

    【讨论】:

    • 您创建一个包含这些结构的新数组,并使用自定义比较函数对其进行排序。如果您必须访问原始数组(而不是底层数据),您可以维护从新数组到旧数组的指针或索引,您可以在排序后使用。
    【解决方案5】:

    您可以使用以下技巧:

    int buffcmp(const void *b1, const void *b2)
    {
        static int bsize=-1;
        if(b2==NULL) {bsize=*(int*)(b1); return 0;}
        return memcmp(b1, b2, idsize);
    }
    

    您首先将其调用为buffcmp(&amp;bsize, NULL),然后将其作为比较函数传递给qsort

    您当然可以通过添加更多if 语句使比较在buffcmp(NULL, NULL) 等情况下表现得更自然。

    【讨论】:

      【解决方案6】:

      您可以使用仿函数(将长度指定给仿函数的构造函数)或 Boost.Lambda(就地使用长度)。

      【讨论】:

        【解决方案7】:

        我不清楚你在问什么。但我会尝试,假设

        • 您只有一个缓冲区
        • 你有一个某种指针数组,它已经以某种方式处理,因此它的部分或全部内容指向缓冲区

        这相当于代码:

        char *buf = (char*)malloc(sizeof(char)*bufsize);
        for (int i=0; i<bufsize; ++i){
            buf[i] = some_cleverly_chosen_value(i);
        }
        
        char *ary[arraysize] = {0};
        for(int i=0; i<arraysize; ++i){
           ary[i] = buf + some_clever_function(i);
        }
        
        /* ...do the sort here */
        

        现在如果你控制缓冲区的分配,你可以替换

        char *buf = (char*)malloc(sizeof(char)*(bufsize+1));
        buf[bufsize]='\0';
        

        然后继续使用 strcmp。即使您不控制缓冲区的填充,这也是可能的。

        如果你不得不忍受别人给你的缓冲区,你可以

        1. 使用一些全局存储(您要求避免和良好的想法)。
        2. 使用比原始指针(支持额外数据的结构或类的地址)更复杂的排序函数。为此,您需要控制上述代码中ary 的定义。
        3. 使用支持额外输入的排序功能。 Adam 建议的 sort_r 或家庭推出的解决方案(我建议将其作为学生的练习,在现实生活中不推荐)。在任何一种情况下,额外的数据都可能是指向缓冲区末尾的指针。

        【讨论】:

        • 什么?!?问题陈述不清楚。一个字节?两个字节?直到缓冲区的末尾?那个指针的值?所有这些都是对威尔所写内容的有效理解。
        • 第二句:“我想根据指针指向的缓冲区中的字节对指针进行排序。” OP 正在询问如何将缓冲区大小传递给排序例程的比较函数。您的回答在这方面没有任何帮助。
        • 我真的一点也不明白。我确信他的意思是从指针到结尾,因为他没有抱怨其他使用该含义的答案,但是......无论如何,我会在此基础上制定一个答案。
        【解决方案8】:

        memcmp 应该在第一个不相等的字节处停止,因此长度应该很大,即到缓冲区的末尾。那么它可以返回零的唯一方法是它确实到达缓冲区的末尾。

        (顺便说一句,我自己倾向于合并排序。它稳定且表现良好。)

        【讨论】:

        • 是的,但是 memcmp() 怎么知道缓冲区有多大呢?这就是手头的问题。
        • 我猜这只是管道问题。编写您自己的归并排序并传递您关心的任何信息是再糟糕不过的事情了。
        猜你喜欢
        • 2011-06-28
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-02
        • 1970-01-01
        相关资源
        最近更新 更多