使用 qsort 时出现分段错误
Segmentation fault when using qsort
我正在开发一个 c 程序来读取 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
中,我不知道为什么。
谁能给我一些建议?
char s[255][255];
这闻起来很难闻。使用 C dynamic memory allocation.
改为考虑指向堆分配字符串数组的堆分配指针。
qsort(s, n , sizeof(char *), cmp);
仔细阅读 qsort(3) 的文档。对于 char s[255][255]
,您对 qsort
的调用是 非常 错误。
Anyone can give me some suggestions?
更仔细地阅读一本优秀的 C 编程书籍和您正在使用的每个函数的文档(甚至 strcmp(3)). Look also at some C reference and glance into the C11 specification n1570。
编译所有警告和调试信息,即 gcc -Wall -Wextra -g
和 GCC (read more about Invoking GCC). Use the debugger gdb
(read about Debugging with GDB)
PS。我们不会做你的功课。
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);
}
比较函数不正确,因为你正在对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;
}
很多人都给出了很好的答案。
这里是你自己找到它的方法,一步一步,使用标准的 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 库对 您的 函数 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
= "jk[=15=]0[=15=]0[=15=]0[=15=]0[=15=]0[=15=]0[=15=]0"
所以我们发现它是一个包含 jk
.
的空终止字符串
所以你的转换无效:*(const char **) p1
。
这应该是 (const char*) p1
。
我们改变演员表,然后就可以了。
我正在开发一个 c 程序来读取 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
中,我不知道为什么。
谁能给我一些建议?
char s[255][255];
这闻起来很难闻。使用 C dynamic memory allocation.
改为考虑指向堆分配字符串数组的堆分配指针。
qsort(s, n , sizeof(char *), cmp);
仔细阅读 qsort(3) 的文档。对于 char s[255][255]
,您对 qsort
的调用是 非常 错误。
Anyone can give me some suggestions?
更仔细地阅读一本优秀的 C 编程书籍和您正在使用的每个函数的文档(甚至 strcmp(3)). Look also at some C reference and glance into the C11 specification n1570。
编译所有警告和调试信息,即 gcc -Wall -Wextra -g
和 GCC (read more about Invoking GCC). Use the debugger gdb
(read about Debugging with GDB)
PS。我们不会做你的功课。
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);
}
比较函数不正确,因为你正在对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;
}
很多人都给出了很好的答案。
这里是你自己找到它的方法,一步一步,使用标准的 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 库对 您的 函数 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
= "jk[=15=]0[=15=]0[=15=]0[=15=]0[=15=]0[=15=]0[=15=]0"
所以我们发现它是一个包含 jk
.
所以你的转换无效:*(const char **) p1
。
这应该是 (const char*) p1
。
我们改变演员表,然后就可以了。