关于C语言中的qsort(),**buf和buf[][]的区别

About qsort() in C, difference between ** buf and buf[][]

当我在 Mac 的 C 中使用 qsort() 时,这些代码运行良好,它可以很好地对一个文件中的每一行进行排序。

int compare(const void *p, const void *q) {
    return strcmp(p,q);
}

void function_name(){
        char buf[1024][1024];
        int i=0;
        FILE * fp;
        if(!(fp=fopen(filename,"r"))){
            perror("Open error!");
            exit(0);
        }
        while(fgets(buf[i],1024,fp)){
            //printf("%s",buf[i]);
            i++;
        }
        qsort(buf, i, sizeof(buf[0]), compare);
    }

但是,当我使用 malloc 分配 space 时,它很糟糕。这是为什么? 下面的代码显示了我使用 malloc 来创建一个二维数组。我前后打印buf的内容。好像信息全丢了。

    int i=0;
    FILE * fp;
    char ** buf;
    buf = (char **)malloc(sizeof(char*)*1024);
    if(!(fp=fopen(filename,"r"))){
        perror("Open error!");
        exit(0);
    }
    buf[0]=(char *)malloc(sizeof(char)*1024);
    while(fgets(buf[i],1024,fp)){
        i++;
        buf[i]=(char *)malloc(sizeof(char)*1024);
    }
    for(int j=0;j<i;j++){
        printf("%s",buf[j]);
    }
    printf("hehe%ld\n",sizeof(char)*1024);

    qsort(buf, i, sizeof(char)*1024, compare);

    printf("hehe\n");
    for(int j=0;j<i;j++){
        printf("%s",buf[j]);
    }

输出:

a
A
b
c
d
D
C

E
e
B
d
e
f
a
hehe1024
hehe
(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)

最重要的是,如何修复我的 malloc 版本?

当您尝试将 qsort buf 声明为 char **buf 时,您使用了错误的比较函数。正如我在评论中指出的那样,char buf[x][y]x 字符数组 y 字符 的数组,例如char (*)[y] 作为参数传递时。当您将 buf 声明为 char **buf; 时,您声明了一个指向类型为 char 的指针的 指针。在任何一种情况下,您都有一个指向字符串 指针,并且您必须取消引用传递给 qsort 一个额外间接级别的每个值,例如

int cmpstrings (const void *a, const void *b) {
    return strcmp (*(char * const *)a, *(char * const *)b);
}

使用 char **buf; 的一个简短示例是:

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

/* qsort string comparison - for pointer to pointer to char */
int cmpstrings (const void *a, const void *b) {
    return strcmp (*(char * const *)a, *(char * const *)b);
}

int main (void) {

    char *ap[] = { "This is a tale",
                    "of captian Jack Sparrow",
                    "a pirate so brave",
                    "on the seven seas." },
        **buf = NULL;
    int n = sizeof ap/sizeof *ap;    

    if (!(buf = malloc (n * sizeof *buf))) { /* allocate pointers */
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    for (int i = 0; i < n; i++)
        buf[i] = strdup (ap[i]);    /* allocate/copy strings */

    qsort (buf, n, sizeof *buf, cmpstrings);

    for (int i = 0; i < n; i++) {   /* print and free */
        printf ("buf[%d] : %s\n", i, buf[i]);
        free (buf[i]);
    }
    free (buf);

    return 0;
}

例子

$ ./bin/qsortptp
buf[0] : This is a tale
buf[1] : a pirate so brave
buf[2] : of captian Jack Sparrow
buf[3] : on the seven seas.

qsortmemcpy 和类似函数假定指针传递指向数组。数组的定义是在相邻的存储单元中 连续分配 x 个项目。例如 char array [x][y];.

关于指针到指针的使用存在广泛的混淆。有一个古老的技巧可以用来声明查找 table,其中每个项目都指向一个可变长度的数组,如下所示:

char** ptr = malloc(x * sizeof(char*));
for(int i=0; i<x; i++)
{
  ptr[i] = malloc(y * sizeof(char));
}

这允许您使用类似数组的语法 ptr[i][j] 访问查找 table。但是 并不 意味着这是一个数组,指针到指针和二维数组之间没有任何关系!这是相当 x 个段数,其中每个段分配在堆上的任何位置。

只有你会曾经使用上面的指针到指针技巧的原因是你必须为每个指针设置可变长度查找中的项目 table。如果你不需要它,那么上面的方法很糟糕而且总是不正确——它比真正的数组慢得多,无论是在分配开销、堆碎片还是糟糕的数据缓存使用方面。正如您所发现的,它甚至不能用作数组...因为它不是一个数组。

不幸的是,现在世界上有许多无能的准大师错误地将上述方法教导为动态分配多维数组的方法。这完全是胡说八道。它不是一个多维数组,不能用作一个。 See this 了解您实际上应该如何动态分配多维数组。