【问题标题】:c function pointersc函数指针
【发布时间】:2010-01-12 18:31:29
【问题描述】:

此程序根据传递给 main 的参数按字母/数字顺序对行进行排序。

我现在正在 k&R 做这个练习: 添加选项 -f 将大小写折叠在一起,以便在排序时不区分大小写;例如,a 和 A 比较相等。

我在 my_strcmp 中写的内容好吗?它与 -r 和 -n 一起使用会很好吗? (r - 逆序,n- 数字排序)。

我想 id 在这里征求您的意见,因为在 klc wiki 上,解决方案不是这样呈现的。

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

#define MAXBUF 10000

#define MAXLINES 5000
char *lineptr[MAXLINES];

int readlines(char *lineptr[], char buffer[], int nlines);
void writelines(char *lineptr[], int nlines);

void qsort(void **lineptr, int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);

int reverse = 0;
int numeric = 0;
int fold = 0;
int my_strcmp(char *s1, char *s2)
{

     char *p1 = (reverse) ? s2 : s1;
     char *p2 = (reverse) ? s1 : s2;


     if(fold) {
         while(toupper(*p1) == toupper(*p2)) {
            p1++, p2++;
            if(!*p1)
              return 0;
         }
         return toupper(*p1) - toupper(*p2);
     }

     return strcmp(p1, p2);
}

int main(int argc, char *argv[])
{
    int nlines;
    char buffer[MAXBUF];

    int i = 1;
    for(i = 1; i < argc; i++) {
          if(strcmp(argv[i], "-n") == 0) {
             numeric = 1;
          } else if(strcmp(argv[i], "-r") == 0) {
                 reverse = 1;
          } else if(strcmp(argv[i], "-f") == 0) {
                 fold = 1;
          } else {
                 printf("illegal argument\n");
          }
    }


    if((nlines = readlines(lineptr, buffer, MAXLINES)) >= 0) {
        qsort((void **)lineptr, 0, nlines - 1, 
        (numeric ? (int (*)(void *, void *))numcmp : (int (*)(void *, void *))my_strcmp));
        writelines(lineptr, nlines);
        getchar();
        return 0;
    } else {
           printf("input too big to sort\n");
           return 1;
    }

}

void writelines(char *lineptr[], int nlines)
{
    int i;

    for (i = 0; i < nlines; i++)
        printf("%s\n", lineptr[i]);
}

int getline(char s[], int lim)
{
    int c, i;

    for (i=0; i<lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

#define MAXLEN 1000
int readlines(char *lineptr[], char *buffer, int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];

    nlines = 0;
    p = buffer;
    while ((len = getline(line, MAXLEN)) > 0)
        if (p - buffer + len > MAXBUF)
            return -1;
        else {
            line[len-1] = '\0'; /* delete newline */
            strcpy(p, line);
            lineptr[nlines++] = p;
            p += len;
        }
    return nlines;
}

void qsort(void *v[], int left, int right, int(*comp)(void *, void *))
{
     int i, last;
     void swap(void *v[], int, int);

     if(left >= right)
        return;

     swap(v, left, (left + right) / 2);
     last = left;
     for(i = left + 1; i <= right; i++) 
        if((*comp)(v[i], v[left]) < 0)
           swap(v, ++last, i);
     swap(v, left, last);
     qsort(v, left, last - 1, comp);
     qsort(v, last + 1, right, comp);
}

#include <stdlib.h>

int numcmp(char *p1, char *p2)
{
  char *s1 = reverse ? p2 : p1;
  char *s2 = reverse ? p1 : p2;
  double v1, v2;

  v1 = atof(s1);
  v2 = atof(s2);
  if (v1 < v2)
    return -1;
  else if (v1 > v2)                                     
    return 1;      
  else
    return 0;
}

void swap(void *v[], int i, int j)
{
     void *temp;

     temp = v[i];
     v[i] = v[j];
     v[j] = temp;
}

还有一个问题。我试图添加选项-d(目录顺序)-“它只对字母数字和空格进行比较。确保它与-f结合使用。”我对如何编辑 my_strcmp 有点困惑。这就是我所做的:

int my_strcmp(char *s1, char *s2)
{

     char *p1 = (reverse) ? s2 : s1;
     char *p2 = (reverse) ? s1 : s2;

     if(directory) {
         while(!isdigit(*p1) && !isspace(*p1) && !isalpha(*p1) && *p1)
           p1++;

         while(!isdigit(*p2) && !isspace(*p2) && !isalpha(*p2) && *p2)
           p2++;
     }

     if(fold) {
         while(toupper(*p1) == toupper(*p2)) {
            p1++, p2++;
            if(!*p1)
              return 0;
            if(directory) {
               while(!isdigit(*p1) && !isspace(*p1) && !isalpha(*p1))
                   p1++;

               while(!isdigit(*p2) && !isspace(*p2) && !isalpha(*p2))
                   p2++;         
            }
         }
         return toupper(*p1) - toupper(*p2);
     }

     return my_strcmp2(p1, p2);
}

但我不太确定它是否好。

我还写了 my_strcmp2,它​​处理目录和折叠为零的情况。

然后我们只是 strcmp 它们,但我们还必须跟踪目录是否为 1...

int my_strcmp2(char *s1, char *s2)
{
    char *p1 = (reverse) ? s2 : s1;
    char *p2 = (reverse) ? s1 : s2;

     while(*p1 == *p1 && *p1) {
        p1++;
        p2++;

         if(directory) {
             while(!isdigit(*p1) && !isspace(*p1) && !isalpha(*p1) && *p1)
               p1++;

             while(!isdigit(*p2) && !isspace(*p2) && !isalpha(*p2) && *p2)
               p2++;
         }
     }
     return *p1 - *p2;
}

【问题讨论】:

    标签: c pointers function function-pointers


    【解决方案1】:

    您必须设定自己的成功标准。写下测试用例、两个字符串的集合以及它们应该产生的输出。通过运行代码来验证它们。不要忘记异常值,也传递空字符串和 NULL 字符串。

    【讨论】:

      【解决方案2】:

      fold==1 时,当 p1 是 p2 的前缀时,my_strcmp 将返回 0。您可以通过将行 if(!*p1) return 0 移动到 while 循环的开头来解决此问题。除此之外,它看起来不错。

      编辑: 关于你的第二个问题:你在正确的轨道上增加 p1 和 p2 被忽略的字符。 但是,这个函数在非折叠模式下不起作用(它似乎无限地调用自己)。(这已通过对问题的编辑修复)

      我会创建一个辅助函数compareCharactes,它会根据fold 的值比较2 个字符是否区分大小写。然后,无论fold 是打开还是关闭,您都可以使用您的while 循环。

      Edit2:好的,你不断改变你的问题......无论如何,如果你接受我对compareCharacters 函数的建议,就不需要单独的my_strcmpmy_strcmp2 函数。你可以写while (compareCharacters(*p1, *p2)==0) {.....}

      【讨论】:

        【解决方案3】:

        乍一看,我觉得还可以。

        它是否编译并做正确的事情,编译器和操作系统是比我更好的代码验证器。

        【讨论】:

          【解决方案4】:

          看起来my_strcmp 应该可以工作,但我想我会做一些不同的事情。您确实有三种可能性:区分大小写、不区分大小写和数字比较。我会做类似的事情:

          typedef int (*cmp_func)(void *, void *);
          cmp_func funcs[] = {strcmp, my_strcmp, numcmp};
          
          enum comparison_types = { case_sensitive, case_insensitive, numeric};
          
          int comparison = case_sensitive;
          
          // ...
          
          if (strcmp(argv[i], "-f"))
              comparison = case_insensitive;
          else if (strcmp(argv[i], "-n"))
              comparison = numeric;
          
          // ...
             qsort(/* ... */, funcs[comparison]);
          

          使用它,my_strcmp进行不区分大小写的比较。添加目录顺序比较将涉及添加另一个单独的函数,而不是在 my_strcmp 上扩展(进一步),这已经非常大,并且已经做了两个不同的事情(并且通过目录比较,将做三个或六个包括反向比较)。

          【讨论】:

          • 如果可以组合不同的标志(或者您需要为每个可能的组合编写一个函数),那将不会真正起作用。
          • @interjay:确实,如果您可以以不同的方式组合大量标志,这将变得笨拙。另一方面,至少对于到目前为止所问的问题,我认为这是最干净的方法。我还注意到很多可以任意组合的标志在任何情况下最终都会导致很多代码路径,所以这主要是你如何打包它们的问题。如果不同的路径可以共享大量代码,那么将它们组合成一个函数是值得的——但是(例如)区分大小写和区分大小写的比较几乎没有任何共享。