qsort 分段错误结构
qsort Segmentation Fault structs
所以,我的第一个问题,请耐心等待我:
我的任务是对结构数组(名字、姓氏和生日的另一个结构,由年、月、日组成)进行排序。我必须按生日排序并使用 qsort。
我的问题是,我查阅了有关 qsort 的所有内容,但我不太确定我的实现是否正确,因为我是 C 的新手。我可以创建可执行程序,但它不会只给我任何结果分段错误。
这是我的代码:
#include <stdio.h>
#include <stdlib.h>
typedef int (*compfn) (const void*, const void*);
typedef struct {
unsigned year, month, day;
} date_t;
typedef struct {
char name[32];
char surname[32];
date_t birthday;
}person_t;
typedef struct {
unsigned n;
unsigned cap;
person_t *arr;
} persons_t;
int compare(person_t *a, person_t *b){
if(a->birthday.year!=b->birthday.year){
return a->birthday.year-b->birthday.year;
}else{
if(a->birthday.month!=b->birthday.month){
return a->birthday.month-b->birthday.month;
}else{
return a->birthday.day-b->birthday.day;
}
}
}
int main(int argc, char* argv[])
{
if (argc <= 1) {
fprintf(stderr, "syntax: %s <inputfile>\n", argv[0]);
return 1;
}
FILE* f = fopen(argv[1], "rt");
if (f == NULL) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
persons_t persons;
persons.n = 0;
persons.cap = 0;
persons.arr = NULL;
person_t p;
while (fscanf(f, "%s %s %4u-%2u-%2u", p.name, p.surname,
&p.birthday.year, &p.birthday.month, &p.birthday.day) == 5) {
if (persons.n == persons.cap) {
persons.cap = persons.cap == 0 ? 1 : 2 * persons.cap;
persons.arr = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
}
persons.arr[persons.n++] = p;
}
int nitems = persons.cap*sizeof(persons.arr[0]);
int size = sizeof(persons.arr[0]);
qsort(persons.arr, nitems, size, (compfn)compare);
for (unsigned i = 0; i < persons.n; i++) {
person_t *p = persons.arr + i;
printf("%s %s %4u-%2u-%2u\n",
p->name, p->surname,
p->birthday.year, p->birthday.month, p->birthday.day);
}
fclose(f);
return 0;
}
希望有人能帮助我,
提前致谢 ;)
因此,您使用 persons.cap 通过在需要时将其大小加倍来分配我们的数组,但您并未填充其所有元素,是吗?
从你的代码来看,实际人数是nitems = persons.n,而不是persons.cap。如果您使用 nitems=persons.n?
重试您的代码会怎样
如果你的数组中有未填充的元素,这意味着它们里面的字符串是任意的(即 person.name),所以可能不是空终止的,当你试图显示它们时会发生崩溃。
就 _t
后缀的标识符而言,根据 C 标准,它们是为实现保留的(例如,您的编译器,and/or 您的标准库)。您的实现很可能已经具有 date_t
类型,并且您的代码可能会导致某种恶作剧。如果您希望避免微妙而危险的标识符冲突造成各种破坏,最好避免它们。不用担心,您始终可以使用“_s
”来表示 struct
类型!
每当你声明一个代表数组中的索引的变量时,使用 size_t
作为类型!
int compare(person_t *a, person_t *b){
...
qsort(persons.arr, nitems, size, (compfn)compare);
根据 qsort manual,作为比较器函数给出的参数应该是一个 int (*compar)(const void *, const void *)
,这就是你转换为 (compfn)
后给出的参数。据 qsort
所知,函数接受两个 const void *
参数,这可能与 person_t *
参数的表示不同。这肯定会导致段错误。不要在 compare
的类型上撒谎。将其更改为更像:
int compare(const void *x, const void *y) {
const person_s *a = x, *b = y;
/* ... */
}
...而且您不需要强制转换 或 类型定义。
接下来,介绍该函数的 return 值。我使用过一些实现,其中词法不合逻辑的 return 值会导致分段错误。例如,如果 a <= b
和 b <= c
,则 a <= c
,但您的代码不能保证这一点。事实上,使用您的代码,a <= b
、b <= c
和 a > c
是可能的。我建议确保您的代码保证 return 值和词汇顺序之间的对应关系。您可以通过 returning 1 表示大于,0 表示等于或 -1 表示小于。
#define lexical_order(x,y) ((x > y) - (x < y))
int compare(const void *x, const void *b){
const person_s *a = x, *b = y;
return a->birthday.year != b->birthday.year ? lexical_order(a->birthday.year, b->birthday.year)
: a->birthday.month != b->birthday.month ? lexical_order(a->birthday.month, b->birthday.month)
: lexical_order(a->birthday.day, b->birthday.day);
}
我相信您知道您应该检查 realloc
的 return 值...例如:
void *temp = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
if (temp == NULL) { /* If we don't check return value prior *
* to assigning to persons.arr, we *
* might leak some memory... */
puts("Error in realloc");
free(persons.arr);
exit(-1);
}
persons.arr = temp;
最后,也是最重要的(这可能是你的错误),你确定吗?
int nitems = persons.cap*sizeof(persons.arr[0]);
如果你想把这个作为项目数传递给qsort
(这是通常的),那么我认为应该是:
size_t nitems = persons.n;
P.S。如果您第二次错过了它,您可能应该审核您的代码以确保您使用 size_t
仅存储数组索引。
P.P.S。不要忘记在程序末尾使用 free(persons);
,这样当您使用 valgrind
...
时就不会收到内存泄漏的报告
P.P.P.S。 valgrind
太棒了!
所以,我的第一个问题,请耐心等待我:
我的任务是对结构数组(名字、姓氏和生日的另一个结构,由年、月、日组成)进行排序。我必须按生日排序并使用 qsort。 我的问题是,我查阅了有关 qsort 的所有内容,但我不太确定我的实现是否正确,因为我是 C 的新手。我可以创建可执行程序,但它不会只给我任何结果分段错误。
这是我的代码:
#include <stdio.h>
#include <stdlib.h>
typedef int (*compfn) (const void*, const void*);
typedef struct {
unsigned year, month, day;
} date_t;
typedef struct {
char name[32];
char surname[32];
date_t birthday;
}person_t;
typedef struct {
unsigned n;
unsigned cap;
person_t *arr;
} persons_t;
int compare(person_t *a, person_t *b){
if(a->birthday.year!=b->birthday.year){
return a->birthday.year-b->birthday.year;
}else{
if(a->birthday.month!=b->birthday.month){
return a->birthday.month-b->birthday.month;
}else{
return a->birthday.day-b->birthday.day;
}
}
}
int main(int argc, char* argv[])
{
if (argc <= 1) {
fprintf(stderr, "syntax: %s <inputfile>\n", argv[0]);
return 1;
}
FILE* f = fopen(argv[1], "rt");
if (f == NULL) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
persons_t persons;
persons.n = 0;
persons.cap = 0;
persons.arr = NULL;
person_t p;
while (fscanf(f, "%s %s %4u-%2u-%2u", p.name, p.surname,
&p.birthday.year, &p.birthday.month, &p.birthday.day) == 5) {
if (persons.n == persons.cap) {
persons.cap = persons.cap == 0 ? 1 : 2 * persons.cap;
persons.arr = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
}
persons.arr[persons.n++] = p;
}
int nitems = persons.cap*sizeof(persons.arr[0]);
int size = sizeof(persons.arr[0]);
qsort(persons.arr, nitems, size, (compfn)compare);
for (unsigned i = 0; i < persons.n; i++) {
person_t *p = persons.arr + i;
printf("%s %s %4u-%2u-%2u\n",
p->name, p->surname,
p->birthday.year, p->birthday.month, p->birthday.day);
}
fclose(f);
return 0;
}
希望有人能帮助我, 提前致谢 ;)
因此,您使用 persons.cap 通过在需要时将其大小加倍来分配我们的数组,但您并未填充其所有元素,是吗?
从你的代码来看,实际人数是nitems = persons.n,而不是persons.cap。如果您使用 nitems=persons.n?
重试您的代码会怎样如果你的数组中有未填充的元素,这意味着它们里面的字符串是任意的(即 person.name),所以可能不是空终止的,当你试图显示它们时会发生崩溃。
就 _t
后缀的标识符而言,根据 C 标准,它们是为实现保留的(例如,您的编译器,and/or 您的标准库)。您的实现很可能已经具有 date_t
类型,并且您的代码可能会导致某种恶作剧。如果您希望避免微妙而危险的标识符冲突造成各种破坏,最好避免它们。不用担心,您始终可以使用“_s
”来表示 struct
类型!
每当你声明一个代表数组中的索引的变量时,使用 size_t
作为类型!
int compare(person_t *a, person_t *b){
...
qsort(persons.arr, nitems, size, (compfn)compare);
根据 qsort manual,作为比较器函数给出的参数应该是一个 int (*compar)(const void *, const void *)
,这就是你转换为 (compfn)
后给出的参数。据 qsort
所知,函数接受两个 const void *
参数,这可能与 person_t *
参数的表示不同。这肯定会导致段错误。不要在 compare
的类型上撒谎。将其更改为更像:
int compare(const void *x, const void *y) {
const person_s *a = x, *b = y;
/* ... */
}
...而且您不需要强制转换 或 类型定义。
接下来,介绍该函数的 return 值。我使用过一些实现,其中词法不合逻辑的 return 值会导致分段错误。例如,如果 a <= b
和 b <= c
,则 a <= c
,但您的代码不能保证这一点。事实上,使用您的代码,a <= b
、b <= c
和 a > c
是可能的。我建议确保您的代码保证 return 值和词汇顺序之间的对应关系。您可以通过 returning 1 表示大于,0 表示等于或 -1 表示小于。
#define lexical_order(x,y) ((x > y) - (x < y))
int compare(const void *x, const void *b){
const person_s *a = x, *b = y;
return a->birthday.year != b->birthday.year ? lexical_order(a->birthday.year, b->birthday.year)
: a->birthday.month != b->birthday.month ? lexical_order(a->birthday.month, b->birthday.month)
: lexical_order(a->birthday.day, b->birthday.day);
}
我相信您知道您应该检查 realloc
的 return 值...例如:
void *temp = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
if (temp == NULL) { /* If we don't check return value prior *
* to assigning to persons.arr, we *
* might leak some memory... */
puts("Error in realloc");
free(persons.arr);
exit(-1);
}
persons.arr = temp;
最后,也是最重要的(这可能是你的错误),你确定吗?
int nitems = persons.cap*sizeof(persons.arr[0]);
如果你想把这个作为项目数传递给qsort
(这是通常的),那么我认为应该是:
size_t nitems = persons.n;
P.S。如果您第二次错过了它,您可能应该审核您的代码以确保您使用 size_t
仅存储数组索引。
P.P.S。不要忘记在程序末尾使用 free(persons);
,这样当您使用 valgrind
...
P.P.P.S。 valgrind
太棒了!