C-使用通用函数将多个数组排序为一个

时间:2018-06-25 09:22:41

标签: c arrays sorting

我需要使用通过椭圆获取参数的通用函数将多个数组排序为一个数组。

我试图将所有元素放入一个数组中,对其进行排序,然后将结果推送到以下数组中。

这是该函数的原型:

void gSortAll(int n, int m, int(*cmp)(void*,void*),void* base,...);

int n :每个数组中的元素数。

int m :数组中每个元素的大小(以字节为单位)。

cmp :用于在特定类型之间进行比较的比较功能。

void * base :要排序的第一个数组,后面是其他带有椭圆然后是NULL的数组。

假设我使用以下数组调用该函数:

int a[] = {17,2,7,8,1};
int b[] = {3,6,5,14,11};
int c[] = {12,9,10,1,4};

排序功能后,数组应为:

int a[] = {1,1,2,3,4};
int b[] = {5,6,7,8,9};
int c[] = {10,11,12,14,17};

我已经开始键入该函数:

int cmpInt(void *a, void *b) {
    return *(int*)a > *(int*)b;
}

void swap(void* p, void* q, int size) { 
    char *pt = (char *)p;
    char *qt = (char *)q;
    char c;
    while(size--) {
        c = *pt;
        *pt = *qt;
        *qt = c;
        pt++;
        qt++;
    }
}

void insert(void* from, void* to, int size) { 
    char *pt = (char *)from;
    char *qt = (char *)to;
    while(size--) {
        *qt = *pt;
        pt++;
        qt++;
    }
}

void sort(void* arr, int size, int sizeType, int(*f)(void *, void *)) {
    int i,j;
    for (i=0; i<size; i++) {
        for (j=0; j<size-1; j++) {
            if(f((char*)arr+j*sizeType,(char*)arr+j*sizeType+sizeType)>0) {
                swap((char*)arr+j*sizeType,(char*)arr+j*sizeType+sizeType, sizeType);
            }
        }
    }
}

// n: number of elements in array
// m: size in bytes of element
// cmp: compare function
// base: the first array
void gSortAll(int n, int m, int(*cmp)(void*,void*),void* base,...) {
    void** arr = malloc(0);
    int count = 0;
    int i;
    void* item;
    va_list param;
    va_start(param, base);
    for(i=0;i<n;i++) {
        arr = realloc(arr, m*(count+1));
        arr[count] = (char*)base+i*m;
        count++;
    }
    va_start(param, base);
    while((item=va_arg(param, void*))!=NULL) {
        for(i=0;i<n;i++) {
            arr = realloc(arr, m*(count+1));
            arr[count] = (char*)item+i*m;
            count++;
        }
    }
    va_end(param);
    sort(arr, count, m, cmp);
    va_start(param, base);
    for(i=0;i<n;i++) insert(arr+i*m, base+i*m, m);
    va_start(param, base);
    while((item=va_arg(param, void*))!=NULL) {
        for(i=0;i<n;i++) insert(arr+i*m, item+i*m, m);
    }
    va_end(param);
}

int main() {
    int a[] = {17,2,7,8,1};
    int b[] = {3,6,5,14,11};
    int c[] = {12,9,10,1,4};
    gSortAll(sizeof(a)/sizeof(a[0]), sizeof(a[0]), *cmpInt, a, b, c, NULL);
    return 0;
}

任何帮助将不胜感激!

4 个答案:

答案 0 :(得分:2)

逐字使用您的sort代码(以及部分但不是全部支持功能),这是gSortAll()的一种变体,其签名略有不同。它避免了显式的void *base参数,因此不必特别对待。

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

static void sort(void *arr, int size, int sizeType, int (*f)(void *, void *));

/* Changed (simplified) function signature */
static void gSortAll(int n, int m, int (*cmp)(void *, void *), ...)
{
    void *item;
    va_list param;

    /* How many arrays? */
    va_start(param, cmp);
    size_t num_arrays = 0;
    while ((item = va_arg(param, void *)) != 0)
        num_arrays++;
    va_end(param);

    if (num_arrays == 0)
        return;

    void *arr = malloc(num_arrays * n * m);
    if (arr == 0)
        return;

    /* Copy data into allocated array */
    va_start(param, cmp);
    void *data = arr;
    while ((item = va_arg(param, void *)) != 0)
    {
        memmove(data, item, n * m);
        data = (char *)data + n * m;
    }
    va_end(param);

    sort(arr, n * num_arrays, m, cmp);

    /* Copy data from allocated array */
    va_start(param, cmp);
    data = arr;
    while ((item = va_arg(param, void *)))
    {
        memmove(item, data, n * m);
        data = (char *)data + n * m;
    }
    va_end(param);

    free(arr);
}

static int cmpInt(void *a, void *b)
{
    return *(int *)a > *(int *)b;
}

static void swap(void *p, void *q, int size)
{
    char *pt = (char *)p;
    char *qt = (char *)q;
    char c;
    while (size--)
    {
        c = *pt;
        *pt = *qt;
        *qt = c;
        pt++;
        qt++;
    }
}

static void sort(void *arr, int size, int sizeType, int (*f)(void *, void *))
{
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size - 1; j++)
        {
            if (f((char *)arr + j * sizeType, (char *)arr + j * sizeType + sizeType) > 0)
            {
                swap((char *)arr + j * sizeType, (char *)arr + j * sizeType + sizeType,
                     sizeType);
            }
        }
    }
}

/* Non-generic code */
static void dump_array(const char *tag, size_t size, int data[size])
{
    printf("%s (%zu):  ", tag, size);
    for (size_t i = 0; i < size; i++)
        printf("%5d", data[i]);
    putchar('\n');
}

int main(void)
{
    int a[] = {17, 2, 7, 8, 1};
    int b[] = {3, 6, 5, 14, 11};
    int c[] = {12, 9, 10, 1, 4};

    printf("Before\n");
    dump_array("A", 5, a);
    dump_array("B", 5, b);
    dump_array("C", 5, c);

    gSortAll(sizeof(a) / sizeof(a[0]), sizeof(a[0]), *cmpInt,
             (void *)a, (void *)b, (void *)c, (void *)NULL);

    printf("After\n");
    dump_array("A", 5, a);
    dump_array("B", 5, b);
    dump_array("C", 5, c);

    /* random -n 12 10 99 | commalist -B 4 -w -W3 -n 12 -b 'int w[] = { ' -T ' };' */
    int w[] = {  86,  86,  48,  40,  39,  29,  69,  71,  30,  15,  46,  19, };
    int x[] = {  21,  43,  11,  85,  82,  81,  41,  46,  33,  32,  15,  43, };
    int y[] = {  91,  19,  82,  33,  25,  83,  36,  85,  75,  65,  37,  57, };
    int z[] = {  39,  61,  65,  83,  26,  82,  30,  81,  30,  34,  22,  82, };

    printf("Before\n");
    dump_array("W", 12, w);
    dump_array("X", 12, x);
    dump_array("Y", 12, y);
    dump_array("Z", 12, z);

    gSortAll(sizeof(w) / sizeof(w[0]), sizeof(w[0]), *cmpInt,
             (void *)w, (void *)x, (void *)y, (void *)z, (void *)NULL);

    printf("After\n");
    dump_array("W", 12, w);
    dump_array("X", 12, x);
    dump_array("Y", 12, y);
    dump_array("Z", 12, z);

    return 0;
}

它使用memmove()-如果愿意,可以使用memcpy()-将单独的数组复制到它创建的单个数组中,并将排序后的数据复制回单独的数组。

示例输出:

Before
A (5):     17    2    7    8    1
B (5):      3    6    5   14   11
C (5):     12    9   10    1    4
After
A (5):      1    1    2    3    4
B (5):      5    6    7    8    9
C (5):     10   11   12   14   17
Before
W (12):     86   86   48   40   39   29   69   71   30   15   46   19
X (12):     21   43   11   85   82   81   41   46   33   32   15   43
Y (12):     91   19   82   33   25   83   36   85   75   65   37   57
Z (12):     39   61   65   83   26   82   30   81   30   34   22   82
After
W (12):     11   15   15   19   19   21   22   25   26   29   30   30
X (12):     30   32   33   33   34   36   37   39   39   40   41   43
Y (12):     43   46   46   48   57   61   65   65   69   71   75   81
Z (12):     81   82   82   82   82   83   83   85   85   86   86   91

运行时带有Valgrind的健康单。

如果绝对必须具有void *base参数,则必须在代码内部单独处理该参数,这很愚蠢,因为命名参数对函数用户没有任何好处,并且会给实现带来一定的成本

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

static void sort(void *arr, int size, int sizeType, int (*f)(void *, void *));

/* Changed (simplified) function signature */
static void gSortAll(int n, int m, int (*cmp)(void *, void *), void *base, ...)
{
    void *item;
    va_list param;

    if (base == 0)
        return;

    /* How many arrays? */
    va_start(param, base);
    size_t num_arrays = 1;      // base is counted too
    while ((item = va_arg(param, void *)) != 0)
        num_arrays++;
    va_end(param);

    void *arr = malloc(num_arrays * n * m);
    if (arr == 0)
        return;

    /* Copy data into allocated array */
    void *data = arr;
    memmove(data, base, n * m);
    data = (char *)data + n * m;
    va_start(param, base);
    while ((item = va_arg(param, void *)) != 0)
    {
        memmove(data, item, n * m);
        data = (char *)data + n * m;
    }
    va_end(param);

    sort(arr, n * num_arrays, m, cmp);

    /* Copy data from allocated array */
    data = arr;
    memmove(base, data, n * m);
    data = (char *)data + n * m;
    va_start(param, base);
    while ((item = va_arg(param, void *)))
    {
        memmove(item, data, n * m);
        data = (char *)data + n * m;
    }
    va_end(param);

    free(arr);
}

static int cmpInt(void *a, void *b)
{
    return *(int *)a > *(int *)b;
}

static void swap(void *p, void *q, int size)
{
    char *pt = (char *)p;
    char *qt = (char *)q;
    char c;
    while (size--)
    {
        c = *pt;
        *pt = *qt;
        *qt = c;
        pt++;
        qt++;
    }
}

static void sort(void *arr, int size, int sizeType, int (*f)(void *, void *))
{
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size - 1; j++)
        {
            if (f((char *)arr + j * sizeType, (char *)arr + j * sizeType + sizeType) > 0)
            {
                swap((char *)arr + j * sizeType, (char *)arr + j * sizeType + sizeType,
                     sizeType);
            }
        }
    }
}

/* Non-generic code */
static void dump_array(const char *tag, size_t size, int data[size])
{
    printf("%s (%zu):  ", tag, size);
    for (size_t i = 0; i < size; i++)
        printf("%5d", data[i]);
    putchar('\n');
}

int main(void)
{
    int a[] = {17, 2, 7, 8, 1};
    int b[] = {3, 6, 5, 14, 11};
    int c[] = {12, 9, 10, 1, 4};

    printf("Before\n");
    dump_array("A", 5, a);
    dump_array("B", 5, b);
    dump_array("C", 5, c);

    gSortAll(sizeof(a) / sizeof(a[0]), sizeof(a[0]), *cmpInt,
             (void *)a, (void *)b, (void *)c, (void *)NULL);

    printf("After\n");
    dump_array("A", 5, a);
    dump_array("B", 5, b);
    dump_array("C", 5, c);

    /* random -n 12 10 99 | commalist -B 4 -w -W3 -n 12 -b 'int w[] = { ' -T ' };' */
    int w[] = {  86,  86,  48,  40,  39,  29,  69,  71,  30,  15,  46,  19, };
    int x[] = {  21,  43,  11,  85,  82,  81,  41,  46,  33,  32,  15,  43, };
    int y[] = {  91,  19,  82,  33,  25,  83,  36,  85,  75,  65,  37,  57, };
    int z[] = {  39,  61,  65,  83,  26,  82,  30,  81,  30,  34,  22,  82, };

    printf("Before\n");
    dump_array("W", 12, w);
    dump_array("X", 12, x);
    dump_array("Y", 12, y);
    dump_array("Z", 12, z);

    gSortAll(sizeof(w) / sizeof(w[0]), sizeof(w[0]), *cmpInt,
             (void *)w, (void *)x, (void *)y, (void *)z, (void *)NULL);

    printf("After\n");
    dump_array("W", 12, w);
    dump_array("X", 12, x);
    dump_array("Y", 12, y);
    dump_array("Z", 12, z);

    return 0;
}

此输出与以前相同。

有一个previous version of this question(仅1万个用户),最终导致混乱和删除(但它是由另一个用户ID提交的)。这是我为该问题写的两个答案之一的扩展-由于混淆,两个答案是必须的。

答案 1 :(得分:1)

您需要使自己确信,代码的每一部分实际上都是必需的,并且可以实现其意图。还将有助于将您的代码细分为一些较小的有意义的原语,您可以单独对其进行测试和调试。

提示:

  • 可以一次分配临时空间
  • insert()可以像使用分散复制一样帮助收集复制步骤
  • 有用于执行insert()sort()的库函数

答案 2 :(得分:1)

每个va_end()必须有va_start()对。

在您的gSortAll()函数中,您可以开始一个新的va_start()块,然后以va_end()结束该块:

va_start(param, base);
for(i=0;i<n;i++) {
    arr = realloc(arr, m*(count+1));
    arr[count] = (char*)base+i*m;
    count++;
}
va_start(param, base);

答案 3 :(得分:0)

无需调用* alloc函数即可完成此操作。您需要的是:

  • 一个自定义容器,它可以遍历具有指定数量的元素的指定数量的数组,其中指向这些数组的指针存储在va_list“对象”中。
  • 自定义quicksort函数,它将使用该容器访问数组元素

我已经编写了一个容器arrs,该容器的初始化过程包括数组数量,每个数组中的元素数量,数组成员的大小以及指向第一个数组的指针va_list。
然后,我使用了在网上找到的一些quicksort函数(从here中使用了一个)并重写了它,以便每当调用arr[n]时,它都会调用我的容器成员访问函数。
完整代码在这里:

#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stddef.h>

/* arrs_s --------------------------------------------------------- */

/**
 * arrs_s structure holds numOfArrays arrays each with numOfElemsInArrays of size size
 * starting in variable argument list va
 */
struct arrs_s {
    size_t numOfArrays;
    size_t numOfElemsInArrays;
    size_t membSize;
    va_list va;
};
/**
 * Intializes arrs_s object
 */
void arrs_init(struct arrs_s *t, size_t numOfArrays, size_t numOfElemsInArrays, size_t membSize, va_list va)
{
    assert(t != NULL);
    assert(membSize != 0);
#ifndef NDEBUG
    // assert all arrays in va_list are not NULL
    va_list va2;
    va_copy(va2, va);
    for (size_t i = numOfArrays; i; --i) {
        assert(va_arg(va2, void*) != NULL);
    }
    va_end(va2);
#endif

    t->numOfArrays = numOfArrays;
    t->numOfElemsInArrays = numOfElemsInArrays;
    t->membSize = membSize;
    va_copy(t->va, va);

}
/**
 * Bassically calls va_end
 */
void arrs_fini(struct arrs_s *t)
{
    va_end(t->va);
}
/**
 * Gets the pointer to element number pos
 */
void *arrs_get(struct arrs_s *t, size_t pos)
{
    assert(pos < t->numOfArrays * t->numOfElemsInArrays);
    size_t n = pos/t->numOfElemsInArrays;
    const size_t m = pos%t->numOfElemsInArrays;

    // get n element in a va list
    va_list va;
    va_copy(va, t->va);
    void *ret;
    while (n--) {
        ret = va_arg(va, void*);
    }
    ret = va_arg(va, void*);
    va_end(va);

    assert(ret != NULL);
    return (void*)(&((char*)ret)[t->membSize * m]);
}
/**
 * Get single element size, maybe should be refactored to arrs_membsize...
 */
size_t arrs_size(struct arrs_s *t)
{
    return t->membSize;
}

/* qsort --------------------------------------------------------- */

/**
 * Swap size bytes of memory between a and b
 */
static inline
void gSortAll_swap(void *a, void *b, size_t size)
{
#if 1 || C99_VLA
    char tmp[size];
    memcpy(tmp, a, size);
    memcpy(a, b, size);
    memcpy(b, tmp, size);
#else
    char c;
    char *ca = a;
    char *cb = b;
    while (size--) {
        c = *ca;
        *ca++ = *cb;
        *cb++ = c;
    }
#endif
}

// rewritten from from https://beginnersbook.com/2015/02/quicksort-program-in-c/
static
void qSortAll_in(struct arrs_s *t, int (*cmp)(const void *, const void *),
            size_t first, size_t last)
{
   if (first < last){
      void * const pivot = arrs_get(t, first);
      size_t i = first;
      size_t j = last;

      while (i < j) {
          while (cmp(arrs_get(t, i), pivot) <= 0 && i < last) {
              ++i;
          }
          while (cmp(arrs_get(t, j), pivot) > 0) {
              j--;
          }
          if (i < j) {
              gSortAll_swap(arrs_get(t, i), arrs_get(t, j), arrs_size(t));
          }
      }
      gSortAll_swap(arrs_get(t, j), pivot, arrs_size(t));

      if (j > 0) {
          qSortAll_in(t, cmp, first, j - 1);
      }
      assert(j != SIZE_MAX);
      qSortAll_in(t, cmp, j + 1, last);
   }
}

void qSortAll(size_t numOfArrays, size_t numOfElemsInArrays,
        size_t size, int (*cmp)(const void*, const void *), /*void *base,*/ ...)
{
    assert(cmp != NULL);

    if (numOfArrays == 0 || numOfElemsInArrays == 0)
        return;

    va_list va;
    struct arrs_s t;

    va_start(va, cmp);
    arrs_init(&t, numOfArrays, numOfElemsInArrays, size, va);

    qSortAll_in(&t, cmp, 0, numOfArrays * numOfElemsInArrays - 1);

    arrs_fini(&t);
    va_end(va);
}

/* main -------------------------------------------------------------- */

void printarr(int *a, size_t s)
{
    printf(" %p[%zu] = ", a, s);
    while (s--) {
        printf("%d,", *a++);
    }
    printf("\n");
}

static int cmp(const void *a, const void *b)
{
    const int ia = *(int*)a;
    const int ib = *(int*)b;
    return ia == ib ? 0 : ia < ib ? -1 : 1;
}

int main()
{
    int a[] = {17,2,7,8,1};
    int b[] = {3,6,5,14,11};
    int c[] = {12,9,10,1,4};

#if 0 || TEST
    srand(time(NULL));
    for (size_t i = 0; i < sizeof(a)/sizeof(a[0]); ++i) {
        a[i] = rand()&0xff;
        b[i] = rand()&0xff;
        c[i] = rand()&0xff;
    }
#endif

    printf("Unsorted:\n");
    printarr(a, sizeof(a)/sizeof(a[0]));
    printarr(b, sizeof(b)/sizeof(b[0]));
    printarr(c, sizeof(c)/sizeof(c[0]));
    printf("\n");

    qSortAll(3, 5, sizeof(int), cmp, a, b, c);

    printf("Sorted:\n");
    printarr(a, sizeof(a)/sizeof(a[0]));
    printarr(b, sizeof(b)/sizeof(b[0]));
    printarr(c, sizeof(c)/sizeof(c[0]));
    printf("\n");

    return 0;
}

示例输出:

Unsorted:
 0x1fff000360[5] = 17,2,7,8,1,
 0x1fff000380[5] = 3,6,5,14,11,
 0x1fff0003a0[5] = 12,9,10,1,4,

Sorted:
 0x1fff000360[5] = 1,1,2,3,4,
 0x1fff000380[5] = 5,6,7,8,9,
 0x1fff0003a0[5] = 10,11,12,14,17,