[英]C Quicksort (linked list) segmentation fault
我必须在链接列表(在C中)上创建一个快速排序。 我的第一个和最后一个指针是枢轴(在此代码中,它是列表的第一个元素)。 我必须使用的结构:
typedef struct list_element list_element;
struct list_element {
char *password;
int count;
list_element* next;
};
typedef struct list list;
struct list {
list_element* first;
list_element* last;
};
我有一个包含100个密码和数量的文件。 像这样:password1 123(下一行)password2 435(下一行)password3 133 ...必须在此程序末尾对密码进行排序(根据其数量)。 左右列表不需要任何额外的内存分配,因为我只需要使用下一个指针即可。 (这就是练习中的提示。)
给定的主要功能:
int main(int argc, char** args)
{
if (argc != 2)
{
printf("Nutzung: %s <Dateiname>\n",args[0]);
return 1;
}
list mylist;
init_list(&mylist);
read_data(args[1],&mylist);
qsort_list(&mylist);
printf("Sortierte Liste:\n");
print_list(&mylist);
free_list(&mylist);
return 0;
}
我已经初始化了我的清单:
void init_list(list* mylist)
{
mylist->first = NULL;
mylist->last = NULL;
}
并在末尾插入一个新元素(passwort =文件中的密码,hauefigkeit =文件中的计数):
void insert_list(list_element* le, list* mylist)
{
if (mylist->first != NULL) {
le->next = mylist->last;
mylist->last = le;
le->next= NULL;
}
else {
mylist->last->next = le;
mylist->last = le;
mylist->last->next = NULL;
}
}
从文件读取数据:
void read_data(char* filename, list* mylist)
{
FILE *file_in = fopen(filename, "r");
if (file_in == NULL) {
perror("Could not open input file!");
exit(1);
}
char buffer[999] = "0";
char *passwort = (char*) calloc(1,sizeof(passwort));
int haeufigkeit = 0;
while (fgets(buffer, sizeof(buffer), file_in) != NULL) {
sscanf(buffer, "%s %d", passwort, &haeufigkeit);
list_element* le = (list_element*)calloc(1,sizeof(list_element));
for(int i = 0; i <=100; i++) {
le->password[i] = passwort[i];
}
le->count = haeufigkeit;
le->next = NULL;
insert_list(le, mylist);
}
fclose(file_in);
}
列表分区:
list_element* partition( list* input, list* left, list* right )
{
list_element* pivot = NULL;
if (input->first != NULL) {
list_element* temp;
pivot = input->first;
input->first = input->first->next;
pivot->next = NULL;
left->first = NULL;
right->first = NULL;
while (input->first != NULL) {
if((pivot->count)>(input->first->count)){
temp=input->first->next;
insert_list(input->first, left);
input->first=temp;
}
else {
temp = input->first->next;
insert_list(input->first, right);
input->first = temp;
}
}
}
return pivot;
}
实际的快速排序:
void qsort_list(list* mylist)
{
if(mylist->first == mylist->last){
}
else{
list* left = calloc(1,sizeof(list));
list* right= calloc(1,sizeof(list));
list_element* pivot = partition(mylist, left, right);
qsort_list(left);
qsort_list(right);
if(left->first == NULL){
mylist->first = pivot;
}
else{
mylist->first = left->first;
left->last->next = pivot;
}
if(right->first == NULL){
pivot->next = NULL;
mylist->last = pivot;
}
else{
pivot->next = right->first;
mylist->last = right->last;
}
free(right);
free(left);
}
}
在最后的打印列表中:
void print_list(list* mylist)
{
list_element *elem = mylist->first;
while (elem != NULL) {
printf("%s %d\n", elem->password, elem->count);
elem = elem->next;
}
}
以及免费清单:
void free_list(list* mylist)
{
list_element *current;
list_element *second;
current = mylist->first;
while (current != NULL) {
second = current->next;
free(current);
current = second;
}
}
语法应该可以。 GCC(c99,Wall)编译没有任何问题。
但是存在分段错误。 我已经搜索了几个小时,不知道问题可能在哪里。 也许您可以帮助我解决这个问题。
在前两个答案之后,没有任何分割错误。 但是read_data函数仍然有问题。 该程序无法正确读取密码。 也许我误解了您对阅读功能的回答。 这是当前功能:
void read_data(char* filename, list* mylist)
{
FILE *file_in = fopen(filename, "r");
if (file_in == NULL) {
perror("Could not open input file!");
exit(1);
}
char buffer[999] = "0";
int haeufigkeit = 0;
while (fgets(buffer, sizeof(buffer), file_in) != NULL) {
char passwort[100];
sscanf(buffer, "%s %d", passwort, &haeufigkeit);
list_element* le = (list_element*)
calloc(1,sizeof(list_element));
le->password = passwort;
le->count = haeufigkeit;
le->next = NULL;
insert_list(le, mylist);
}
fclose(file_in);
}
正如Leonardo Alves Machado指出的那样,当遇到C / C ++程序问题时,第一个反射就是使用gdb
类的调试器运行它。 这是基础知识:
gcc -g main.c -o main
gdb main
(gdb) run
注意-g
编译标志:这会将调试信息添加到可执行文件中。
在read_data
,各行
for(int i = 0; i <=100; i++) {
le->password[i] = passwort[i];
}
真的很烦我。 您为passwort
分配了空间(您永远不会释放它),然后尝试将其复制到le->password
,这是一个简单的指针(没有分配空间)。 您实际需要的是使le->password
指向 passwort
,即
le->password = passwort;
在free_list
,在使用以下方法释放passwort
空间之前,请不要忘记释放list_element
空间:
while (current != NULL) {
second = current->next;
free(current->password);
free(current);
current = second;
}
程序遇到的第一个问题是read_data()
没有为passwort
分配足够的空间。 实际上,目前尚不清楚,为什么要动态地分配它,但是鉴于这样做, sizeof(passwort)
是一个指向char的指针的大小(因为那是passwort
的意思),可能是4或8个字节。 稍后,您似乎假定(尝试)将其内容复制到列表元素中时,分配的空间为100字节长。 为什么不简单地将其声明为100个字符的数组?
char passwort[100];
的确,如果您也以相同的方式声明list_element.passwort
,则循环中的密码复制代码将是正确的,尽管有点不习惯。
实际上,正如@Derlin所观察到的那样,该代码是有问题的。 然而,他提出的解决方案是不正确的。 只要在整个例程中只分配一次本地passwort
,就不能使列表元素指向本地passwort
。 然后,所有列表元素将具有相同的密码字符串,这不是您想要的。 如果你希望你的列表元素包含指向密码,因为他们现在这样做,那么你将要移动的声明和分配passwort
内循环,使您获得分配给每个列表元素单独的口令空间。 那么分配le->password = passwort
建议是正确的。
另一个早期的问题是您的insert_list()
函数严重损坏。
首先考虑当您尝试将元素插入到由init_list()
初始化的空列表中时发生的情况。 列表的next
和last
成员都将为null,因此insert_list()
将尝试执行以下代码:
mylist->last->next = le;
mylist->last = le;
mylist->last->next = NULL;
观察到mylist->last
为null,因此第一行通过尝试取消引用null指针来调用未定义的行为。 分割错误是一个非常合理的观察结果。 您可以通过将其中的第一行更改为
mylist->next = le;
现在考虑当您尝试插入非空列表时会发生什么。 在这种情况下,执行以下行:
le->next = mylist->last;
mylist->last = le;
le->next= NULL;
由于您打算在末尾插入新元素(即附加它),因此将新元素的next
指针设置为列表的最后一个元素很奇怪。 以后再用NULL
覆盖该值是特别奇怪的。 您似乎将其向后:您希望将初始的last
元素设置为指向新元素作为其下一个元素,而不是相反:
mylist->last->next = le;
确实,这正是空列表情况下的错误代码,但是当列表为非空时这很好。
总体而言,您的函数还遭受奇怪的缺乏并行性和某些隐藏代码重复的困扰。 我可能会编写整体函数,像这样:
void append_to_list(list_element* le, list* mylist)
{
le->next= NULL;
if (mylist->first != NULL) {
mylist->last->next = le;
mylist->last = le;
}
else {
mylist->first = le;
mylist->last = le;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.