[英]qsort Segmentation Fault structs
所以,我在这里的第一个问题,请耐心等待:
我的任务是对结构体数组进行排序(名称,姓氏和生日的另一个结构体,其中包括年,月,日)。 我必须按生日和使用qsort排序。 我的问题是,我查找了有关qsort的所有内容,但是由于我是C语言新手,所以我不确定我的实现是否正确。我可以创建可执行程序,但它不会给我任何结果,只有Segmentation Fault。
这是我的代码:
#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;
}
我希望有人能帮助我,在此先谢谢;)
因此,您可以使用person.cap在需要的时候通过将数组大小加倍来分配数组,但是您并未填充其所有元素,对吗?
根据您的代码,实际人数是nitems = person.n,而不是person.cap。 如果使用nitems = persons.n重试代码怎么办?
如果数组中有未填充的元素,则意味着其中的字符串是任意的(即person.name),因此可能不是以空字符结尾的,并且当您尝试显示它们时会发生崩溃。
就_t
后缀的标识符而言,根据C标准,它们保留用于实现(例如,编译器和/或标准库)。 您的实现很有可能已经具有date_t
类型,并且您的代码可能会引起某种恶作剧。 如果您希望避免对标识符进行微妙和危险的冲突而造成各种破坏,那么最好避免使用它们。 不用担心,您始终可以使用' _s
'来表示struct
类型!
每当您声明一个表示数组内索引的变量时,请使用size_t
作为类型!
int compare(person_t *a, person_t *b){
...
qsort(persons.arr, nitems, size, (compfn)compare);
根据qsort手册 ,作为比较器函数给出的参数应为int (*compar)(const void *, const void *)
,这就是将其(compfn)
为(compfn)
以来的(compfn)
。 据qsort
知道,该函数接受两个const void *
参数,这在表示person_t *
可能与person_t *
参数不同。 这肯定会导致段错误。 不要说谎compare
的类型。 将其更改为更像:
int compare(const void *x, const void *y) {
const person_s *a = x, *b = y;
/* ... */
}
...,您将不需要强制转换或 typedef。
接下来,转到该函数的返回值。 我使用过这样的实现方式,在这种实现方式中,词汇上不合逻辑的返回值会导致分段错误。 例如,如果a <= b
和b <= c
,则a <= c
,但是您的代码不能保证这一点。 实际上,使用您的代码有可能a <= b
, b <= c
和a > c
。 我建议确保您的代码保证返回值和词法顺序之间的对应关系。 为此,您可以返回大于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
的返回值...例如:
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;
PS:如果您第二次错过了它,则可能应该审核代码,以确保您使用size_t
仅存储数组索引。
PPS不要忘记free(persons);
在程序的末尾,因此当使用valgrind
时,您不会收到关于内存泄漏的valgrind
...
PPPS valgrind
很棒!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.