【问题标题】:Segmentation fault when using qsort使用 qsort 时出现分段错误
【发布时间】:2018-02-15 19:42:04
【问题描述】:

我正在开发一个 程序来读取txt 文件并对字符串进行排序。

data.txt:

jk ef ab cd bc gh fg ij hi de 

这是我的代码:

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

int cmp(const void *p1, const void *p2) {
    return strcmp(*(const char **)p1,  *(const char **)p2);
}

int main() {
    FILE *f = fopen("data.txt", "r");
    char s[255][255];
    char tmp[255];
    int n = 0;

    while (!feof(f)) {
        fscanf(f, "%s", tmp);
        strcpy(s[n], tmp);
        n++;
    }

    fclose(f);

    qsort(s, n, sizeof(char *), cmp);

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

    return EXIT_SUCCESS;
} 

我在 Ubuntu 上运行了代码,但由于段错误而中断。相信这个段错误发生在qsort,我不知道为什么。

谁能给我一些建议?

【问题讨论】:

标签: c c segmentation-fault qsort


【解决方案1】:

比较函数不正确,因为您正在对char的数组进行排序,您可以将指向元素的指针直接传递给strcmp:

int cmp(const void *p1, const void *p2) {
    return strcmp(p1, p2);
}

但是请注意,您的解析循环也不正确:feof() 不是检查文件结尾的正确方法。改用这个:

  n = 0;
  while (n < 255 && fscanf(f, "%254s", s[n]) == 1) {
      n++;
  }

qsort 调用应该指定数组元素的大小:

  qsort(s, n, sizeof(*s), cmp);

这是一个更正的版本:

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

int cmp(const void *p1, const void *p2) {
    return strcmp(p1, p2);
}

int main(void) {
    FILE *f = fopen("data.txt", "r");
    char s[255][255];
    char tmp[255];
    int n = 0;

    if (f == NULL)
        return EXIT_FAILURE;

    while (n < 255 && fscanf(f, "%254s", s[n]) == 1) {
        n++;
    }
    fclose(f);

    qsort(s, n, sizeof(*s), cmp);

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

    return EXIT_SUCCESS;
}

【讨论】:

    【解决方案2】:

    qsort() 将两个指向数组元素的指针传递给比较函数。

    数组元素的类型为char[255]。所以qsort() 的比较函数会传入两个char(*)[255]。

    所以它应该看起来像

    int cmp(const void *p1, const void *p2)
    {
      const char (*ps1)[255] = p1;
      const char (*ps2)[255] = p2;
    
      return strcmp(*ps1, *ps2);
    }
    

    【讨论】:

    • 如果 OP 想要更改数组大小,那将是一个讨厌的函数,对吧?
    • 警告:初始化从指针 tar 获取类型 [-Wdiscarded-qualifiers] 中丢弃 'const' 限定符
    • const char (*ps1)[255] = p1; 使same warning。
    • 顺便说一句,&amp;s[X][0] 作为比较函数的参数传递。它没有类型信息,因为它作为 void 指针传递。您尝试复制该类型,但最终没有使用该信息。您尝试复制该类型,但最终没有使用该信息。 (作为const char * 传递给strcmp)所以它只是一个表格。在使用strncmp 时可能会很有用,例如strncmp(*ps1, *ps2, sizeof(*ps1))。但是,由于这意味着它是有用的,因为它被破坏为 C-String,它会与 OP 的愿望不同。
    【解决方案3】:

    很多人给出了很好的答案。

    您可以通过以下方式自行找到它,一步一步,使用标准 GNU 工具:

    我们假设源文件名为q.c。

    用调试符号编译(注意这里不需要Makefile):

    % make CFLAGS=-g q
    cc -g  q.c  -o q
    

    现在,使用调试器 (gdb) 运行程序:

    % gdb q
    (gdb) run
    Starting program: /usr/home/fenyo/tmp/qs/q
    Program received signal SIGSEGV, Segmentation fault.
    0x00000008009607a6 in strcmp () from /lib/libc.so.7
    

    现在看堆栈帧:

    (gdb) where
    #0  0x00000008009607a6 in strcmp () from /lib/libc.so.7
    #1  0x00000000004009b5 in cmp (p1=0x7ffffffeeb60, p2=0x7ffffffeeb88) at q.c:8
    #2  0x000000080093b834 in qsort () from /lib/libc.so.7
    #3  0x0000000000400af5 in main () at q.c:26
    

    所以您的问题在于 qsort 库调用 your 函数 cmp,该函数使用错误的指针调用 strcmp。

    所以我们从一个堆栈帧开始,到您的 cmp 函数级别:

    (gdb) up
    #1  0x00000000004009b3 in cmp (p1=0x7ffffffeeb60, p2=0x7ffffffeeb88) at q.c:8
    8           return strcmp( *(const char **) p1,  *(const char **) p2);
    

    我们看p1的类型:

    (gdb) ptype p1
    type = void *
    

    由于 p1 是一个指针,我们检查它的内容,显示 10 个前字节:

    (gdb) print (*(char *) p1)@10
    $43 = "jk\000\000\000\000\000\000\000"
    

    所以我们发现它是一个包含jk 的空终止字符串。

    所以你的演员表无效:*(const char **) p1。

    这应该是(const char*) p1。

    我们改变演员阵容,然后它就起作用了。

    【讨论】:

    • Valgrind 在这类事情上也非常有效 - 并且非常适合非交互式。
    【解决方案4】:
    char s[255][255];
    

    这闻起来很糟糕。使用C dynamic memory allocation。

    考虑一个堆分配的指针,指向堆分配的字符串数组。

    qsort(s, n , sizeof(char *), cmp);
    

    仔细阅读qsort(3)的文档。使用char s[255][255],您对qsort 的调用是非常错误的。

    谁能给我一些建议?

    更仔细地阅读一本好的 C 编程书籍和您正在使用的每个函数的文档(甚至是 strcmp(3))。还可以查看一些 C reference 并查看 C11 规范 n1570。

    使用所有警告和调试信息进行编译,即 gcc -Wall -Wextra -g 和 GCC(阅读有关 Invoking GCC 的更多信息)。 使用调试器 gdb(了解Debugging with GDB)

    PS。我们不会做你的功课。

    【讨论】:

    • 您能否推荐一个简短的指南,让您先了解 GDB 的基础,然后再继续使用 GDB 调试?
    • 没有。那东西有一个介绍性的章节。阅读。此外,还有大量关于 GDB 的教程可以在其他地方找到。 GIYF。
    猜你喜欢
    • 2021-10-31
    • 1970-01-01
    • 2017-06-01
    • 1970-01-01
    • 2017-06-06
    • 2017-08-26
    • 2018-06-18
    • 2019-05-10
    • 2016-01-15
    相关资源
    最近更新 更多