如何编写泛型函数来对C中的字符串数组进行排序?

时间:2015-01-21 17:54:42

标签: c arrays sorting macros generic-programming

我正在尝试编写一个通用函数来对不同类型的数据进行排序。我的代码是:

#include<stdio.h>
#define GENERIC_SORT(TYPE) \
TYPE ##_SORT(TYPE a[],int n) \
{  \
int i,j; \
TYPE aux; \
for(i=1;i<n;i++) \
    for(j=n-1;j>=i;j--) \
    if(a[j]<a[j-1]) \
{ \
    aux=a[j]; \
    a[j]=a[j-1]; \
    a[j-1]=aux; \
} \
}
GENERIC_SORT(int)
GENERIC_SORT(float)
GENERIC_SORT(double)
GENERIC_SORT(char)
int main(void)
{
int i,a[]={3,7,5,4,6,1};
int_SORT(a,6);
for(i=0;i<6;i++)
    printf("%d ",a[i]);
return 0;
}

我正在准备考试,在课程中有GENERIC_MAX的示例,它会找到2个值之间的最大值。而且我应该像这样排序......

它适用于intfloatdoublechar。但是如何使用它来对字符串数组(char a[][100]char *a[])进行排序?

4 个答案:

答案 0 :(得分:5)

泛型排序的一个主要示例是C运行时库qsort()。其最通用的属性之一是它使用“比较函数”作为参数传递。

为什么不采用这种策略?虽然大多数比较函数确实是微不足道的,但在访问对象的情况下,它对于解释对象内部的内容是非常宝贵的。

答案 1 :(得分:3)

您需要做的是使用泛型生成C ++模板函数的C等价物。这通常是函数指针和重新转换void*数据的组合,以实现所需的结果。 qsort()函数就是这样做的。下面是代码清单和示例运行from a similar answer of mine from some time back,其中介绍了如何对多种数据类型使用简单的冒泡排序实现。

要将其扩展为任何数据类型,您只需:

  1. 创建您自己的int compareDataType(void* a, void* b)功能
  2. 更新传递给sizeOfElement函数的compareFcnBubbleSort()参数。
  3. 您的方法可能适用于已经定义了比较操作的原始数据类型,但它不适用于像struct等抽象数据类型。

    代码清单


    /*******************************************************************************
     * Preprocessor Directives
     ******************************************************************************/
    #include <stdio.h>   // printf
    #include <stdlib.h>  // calloc
    #include <string.h>  // memcpy
    #include <time.h>    // random seed initialization
    #define ELEMENT_AT(arr, i, w) (((char*)arr) + ((i)*(w)))
    #define BUF_SIZE  (20)
    
    
    /*******************************************************************************
     * Function Prototypes
     ******************************************************************************/
    typedef struct cricket_s {
       char pname[BUF_SIZE];
       char tname[BUF_SIZE];
       int avg;
    } cricket_t;
    
    
    /*******************************************************************************
     * Function Prototypes
     ******************************************************************************/
    /* @functionName: bubbleSort
     * @brief:        Performs a bubble sort on an input array, using a user-
     *                provided function pointer for comparing data types so that
     *                the function can be as generic as possible.
     * @param:        arr: The array to search.
     * @param:        compareFcn: The comparison function to use.
     * @param:        sizeOfElement: The size of a single element in arr
     * @param:        numElements: The number of elements in arr
     */
    void* bubbleSort(void* arr, int (*compareFcn)(void*, void*), size_t sizeOfElement, size_t numElements);
    
    void rand_str(char *dest, size_t length);
    int compareCricketAvg(void *a, void *b);
    int compareCricketPname(void *a, void *b);
    
    /*******************************************************************************
     * Function Definitions
     ******************************************************************************/
    /*----------------------------------------------------------------------------*/
    void* bubbleSort(void* arr, int (*compareFcn)(void*, void*), size_t sizeOfElement, size_t numElements) {
       if (!arr || !compareFcn || !numElements || !sizeOfElement) {
          return NULL;
       }
       int i, j;
       void* tempBuf;
       /* Create a swap buffer */
       if ((tempBuf = calloc(1, sizeOfElement)) == NULL) {
          return NULL;
       }
       /* Sort the list via bubble sort (stable) */
       for (i=0; i<(numElements-1); i++) {
          for (j=0; j<(numElements - i -1); j++) {
             if (compareFcn(ELEMENT_AT(arr, j, sizeOfElement), ELEMENT_AT(arr, j+1, sizeOfElement)) == (-1)) {
                memcpy(tempBuf, ELEMENT_AT(arr, j, sizeOfElement), sizeOfElement);
                memcpy(ELEMENT_AT(arr, j, sizeOfElement), ELEMENT_AT(arr, j+1, sizeOfElement), sizeOfElement);
                memcpy(ELEMENT_AT(arr, j+1, sizeOfElement), tempBuf, sizeOfElement);
             }
          }
       }
       /* Clean up and exit */
       free(tempBuf);
       return arr;
    }
    
    /*******************************************************************************
     * Comparson function s.
     * Returns (-1) if a<b, +1 if a>b, 0 if a==b
     */
    /*----------------------------------------------------------------------------*/
    int compareCricketAvg(void *a, void *b) {
       if (!a || !b) {
          /* Treat bad input as equality */
          return 0;
       }
       int ret;
       if (((cricket_t*)a)->avg < ((cricket_t*)b)->avg) {
          ret = (-1);
       } else if (((cricket_t*)a)->avg > ((cricket_t*)b)->avg) {
          ret = 1;
       } else
          ret = 0;
       return ret;
    }
    
    
    /*----------------------------------------------------------------------------*/
    int compareCricketPname(void *a, void *b) {
       if (!a || !b) {
          /* Treat bad input as equality */
          return 0;
       }
       int ret;
       char *s1, *s2;
       s1 = ((cricket_t*)a)->pname;
       s2 = ((cricket_t*)b)->pname;
       ret = strncmp(s1, s2, BUF_SIZE);
       if (ret > 0) {
          ret = 1;
       } else if (ret < 0) {
          ret = (-1);
       } else {
          ret = 0;
       }
    
       return ret;
    }
    
    
    /*----------------------------------------------------------------------------*/
    void rand_str(char *dest, size_t length) {
        char charset[] = "0123456789"
                         "abcdefghijklmnopqrstuvwxyz"
                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
        while (length-- > 0) {
            size_t index = (double) rand() / RAND_MAX * (sizeof charset - 1);
            *dest++ = charset[index];
        }
        *dest = '\0';
    }
    
    /*******************************************************************************
     * Main Entry Point
     ******************************************************************************/
    /*----------------------------------------------------------------------------*/
    int main(void) {
       srand(time(NULL));   // init random seed
       int numPlayers = 10;
       int i;
    
       /* Dynamically allocate memory for a few teams */
       cricket_t* team;
       if ((team = calloc(numPlayers, sizeof(cricket_t))) == NULL) {
          printf("Memory error\n");
          return (-1);
       }
    
       /* Populate struct values */
       for (i=0; i<numPlayers; i++) {
          team[i].avg = rand() % 1000;
          rand_str(team[i].pname, BUF_SIZE);
          printf("Team %d - Pname:%s - Average:%d\n", i, team[i].pname, team[i].avg);
       }
       printf("\n");
    
       /* Sort the list according to AVG value */
       bubbleSort((void*)team, compareCricketAvg, sizeof(cricket_t), numPlayers);
    
       /* Print sorted team */
       for (i=0; i<numPlayers; i++) {
          printf("Team %d - Pname:%s - Average:%d\n", i, team[i].pname, team[i].avg);
       }
       printf("\n");
    
       /* Sort again, now by pname */
       bubbleSort((void*)team, compareCricketPname, sizeof(cricket_t), numPlayers);
    
       /* Print sorted team */
       for (i=0; i<numPlayers; i++) {
          printf("Team %d - Pname:%s - Average:%d\n", i, team[i].pname, team[i].avg);
       }
       printf("\n");
    
       free(team);
       return 0;
    }
    

    示例运行


    Team 0 - Pname:YY7plBOnjIi7YQTKjgqB - Average:605
    Team 1 - Pname:sKGbl8pIAjHzq6U2UimD - Average:439
    Team 2 - Pname:tBrmmKDNmvf6crrlQaWa - Average:226
    Team 3 - Pname:vBXqESI0vju7KRuvvhS1 - Average:117
    Team 4 - Pname:YdYqzPBv0s0Bqqgi9hNs - Average:209
    Team 5 - Pname:VdDpJ8GB9dAnb0W1Bs14 - Average:633
    Team 6 - Pname:DuUTM3bAvXvJAVsJB3TP - Average:212
    Team 7 - Pname:h1Fd2hF3l8GQ2AD6LdBI - Average:237
    Team 8 - Pname:kjEN3gRX5ve6ar8r7cMg - Average:467
    Team 9 - Pname:Djtgpet1XdmhSal81iew - Average:473
    
    Team 0 - Pname:VdDpJ8GB9dAnb0W1Bs14 - Average:633
    Team 1 - Pname:YY7plBOnjIi7YQTKjgqB - Average:605
    Team 2 - Pname:Djtgpet1XdmhSal81iew - Average:473
    Team 3 - Pname:kjEN3gRX5ve6ar8r7cMg - Average:467
    Team 4 - Pname:sKGbl8pIAjHzq6U2UimD - Average:439
    Team 5 - Pname:h1Fd2hF3l8GQ2AD6LdBI - Average:237
    Team 6 - Pname:tBrmmKDNmvf6crrlQaWa - Average:226
    Team 7 - Pname:DuUTM3bAvXvJAVsJB3TP - Average:212
    Team 8 - Pname:YdYqzPBv0s0Bqqgi9hNs - Average:209
    Team 9 - Pname:vBXqESI0vju7KRuvvhS1 - Average:117
    
    Team 0 - Pname:vBXqESI0vju7KRuvvhS1 - Average:117
    Team 1 - Pname:tBrmmKDNmvf6crrlQaWa - Average:226
    Team 2 - Pname:sKGbl8pIAjHzq6U2UimD - Average:439
    Team 3 - Pname:kjEN3gRX5ve6ar8r7cMg - Average:467
    Team 4 - Pname:h1Fd2hF3l8GQ2AD6LdBI - Average:237
    Team 5 - Pname:YdYqzPBv0s0Bqqgi9hNs - Average:209
    Team 6 - Pname:YY7plBOnjIi7YQTKjgqB - Average:605
    Team 7 - Pname:VdDpJ8GB9dAnb0W1Bs14 - Average:633
    Team 8 - Pname:DuUTM3bAvXvJAVsJB3TP - Average:212
    Team 9 - Pname:Djtgpet1XdmhSal81iew - Average:473
    

答案 2 :(得分:1)

通过使用char *定义新类型,您可以绕过struct pointtypedef等非符号类型的问题。更好的方法可能是将新函数的名称作为附加参数传递给宏。

比较问题可以通过传递比较标准来解决,就像其他人指向你的qsort的回调函数一样。因为函数没有真正调用,但在编译时被替换,它可以是一个宏。

这是你的宏扩展:

#define GENERIC_SORT(NAME, TYPE, LT)            \
    void NAME(TYPE a[], int n)                  \
    {                                           \
        int i, j;                               \
                                                \
        for (i = 1; i < n; i++) {               \
            for (j = n - 1; j >= i; j--) {      \
                if (LT(a[j], a[j - 1])) {       \
                    TYPE aux = a[j];            \
                    a[j] = a[j - 1];            \
                    a[j - 1] = aux;             \
                }                               \
            }                                   \
        }                                       \
    }

您的整数排序是:

#define LESS(a, b) ((a) < (b))

GENERIC_SORT(int_sort, int, LESS);

int main(void)
{
    int array[] = {
        6, 3, 9, 2, 7, 10, 5, 1
    };
    int n = sizeof(array) / sizeof(*array);
    int i;

    int_sort(array, n);
    for (i = 0; i < n; i++) {
        printf("%d\n", array[i]);
    }

    return 0;
}

使用比较函数对字符串进行排序:

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

#include "gen.h"   /* your macro */

int str_less(const char *a, const char *b)
{
    return strcmp(a, b) < 0;
}

GENERIC_SORT(str_sort, const char *, str_less);

int main(void)
{
    const char *array[] = {
        "apricot", "orange", "banana", "apple", "papaya", "kiwi"
    };
    int n = sizeof(array) / sizeof(*array);
    int i;

    str_sort(array, n);
    for (i = 0; i < n; i++) {
        printf("%s\n", array[i]);
    }

    return 0;
}

答案 3 :(得分:0)

如果您已经拥有GENERIC_MAX代码(如评论中所述),则可以使用它代替比较和交换。要做到这一点,定义GENERIC_MIN(应该很容易 - 非常类似于泛型max)。然后:

#define GENERIC_SORT(TYPE) \
TYPE ##_SORT(TYPE a[],int n) \
{  \
int i,j; \
TYPE aux_min, aux_max; \
for(i=1;i<n;i++) \
    for(j=n-1;j>=i;j--) \
{ \
    aux_min=GENERIC_MIN(a[j], a[j-1]); \
    aux_max=GENERIC_MAX(a[j], a[j-1]); \
    a[j-1]=aux_min; \
    a[j]=aux_max; \
} \
}