C - 对动态大小的字符串数组进行排序

C - Sorting a dynamically sized array of strings

我是 C 的新手,正在尝试从文件中读取输入数据流,将其附加到动态大小的数组,这在这一点上效果很好。 之后,我想按字母顺序对数组进行排序。我阅读了 strcmp 的联机帮助页,认为这是执行此操作的最佳方法。但是,每当我尝试执行它时,我都会遇到“分段错误”。分配内存时我到底做错了什么? 这是我的代码供参考:

int main (int argc, char* argv[]) {
    int c = getLineCount();

    FILE * wordlist = fopen("test", "r");
    // If file doesn't exist, handle error
    if ( wordlist == NULL ) {
        fputs("Unable to open input file", stderr);
        exit(1);
    }

    int length = 101;
    char line[length];
    int i = 0;
    char **words = malloc(c * sizeof(line));
    if ( words == NULL ) {
        fputs("Unable to allocate Memory", stderr);
        exit(1);
    }

    while (1) {
        char *l = fgets(line, length, wordlist);
        
        if ( (l == NULL) ) {
            // Check if EOF is reached
            if (feof(wordlist)) {
                // fputs("--- EOF ---\n", stdout);
                break;
            // Check if error occured while reading
            } else {
                fputs("Error reading file", stderr);
                exit(1);
            }
        } else if (strchr(line, '\n') == NULL) {
            // Check if line is too long
            // Iterate until newline is found or until EOF
            int c;
            while((c = fgetc(wordlist)) != '\n' && c != 0);
            fputs("--- LINE TOO LONG ---\n", stderr);
            continue;
        } else if ( line[0] == '\n' ) {
            // Check if line is only "\n", if yes, ignore the line
            continue;
        } else {
            words[i] = malloc(sizeof(line) * sizeof(char));
            if ( words[i] == NULL ) {
                fputs("Unable to allocate Memory", stderr);
                exit(1);
            }
            strcpy(words[i], line);
            i++;
        }
    }
    // Close file
    fclose(wordlist);

    char temp[101];
    for (int i = 0; i < (length-1); i++) {
        int lowest = i;
        for (int j = i+1; j < length; j++) {
            if (strcmp(words[j], words[lowest]) < 0) {
                lowest = j;
            }
        }
        if (lowest != i) {
            strcpy(temp, words[i]);
            words[i] = words[lowest];
            free(words[lowest]);
            words[lowest] = malloc(sizeof(temp) * sizeof(char));
            if ( words[lowest] == NULL ) {
                fputs("Unable to allocate Memory", stderr);
                exit(1);
            }
            strcpy(words[lowest], temp);
        }
    }

    // Print out array
    fputs("--- ARRAY ---\n\n", stdout);
    for (int i = 0; i < c; i++) {
        fputs(words[i], stdout);
    }
    
    exit(0);
}

排序算法的上限是length,这是不正确的,因为length是输入行缓冲区的大小

for (int i = 0; i < (length-1); i++) {

上限应该是 i 来自这里的外部范围,因为当新行添加到数组时它会递增:

strcpy(words[i], line);
i++;

这个上限

for (int i = 0; i < c; i++) {

也应该更改,因为有效行数可能与预期行数不匹配。

完成后不要忘记释放内存。


这两行很快造成内存泄漏(words[i] 的原始指针值丢失),然后设置一个 use-after-free 错误,因为 words[i] 的新值是与 words[lowest] 相同的指针值,已被释放。

words[i] = words[lowest];
free(words[lowest]);

这里不需要额外的缓冲区、(解除)分配和字符串复制。例如,如果您对整数数组进行排序,只需交换指针值即可。

char *tmp = words[i];
words[i] = words[lowest];
words[lowest] = tmp;

这是一个常见的粗略示例,没有错误处理,使用 strdup。它应该说明交换指针值,并确定数组的长度

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

#define MAX_LINES 256

int main(void) {
    char line[4096];
    char **lines = malloc(sizeof *lines * MAX_LINES);
    size_t len = 0;

    while (len < MAX_LINES && fgets(line, sizeof line, stdin))
        lines[len++] = strdup(line);

    for (size_t i = 0; i < len - 1; i++) {
        size_t lowest = i;

        for (size_t j = i + 1; j < len; j++)
            if (strcmp(lines[j], lines[lowest]) < 0)
                    lowest = j;

        if (lowest != i) {
            char *tmp = lines[i];
            lines[i] = lines[lowest];
            lines[lowest] = tmp;
        }
    }

    for (size_t i = 0; i < len; i++) {
        printf("%s", lines[i]);
        free(lines[i]);
    }

    free(lines);
}

stdin:

one
two
hello
foo
world
^D

stdout:

foo
hello
one
two
world