C快速排序(链表)分段错误
Posted
技术标签:
【中文标题】C快速排序(链表)分段错误【英文标题】:C Quicksort (linked list) segmentation fault 【发布时间】:2017-01-30 19:00:57 【问题描述】:我必须在链表上创建一个快速排序(在 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 个密码和计数的文件。 像这样: 密码1 123(下一行)密码2 435(下一行)密码3 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;
并在末尾插入一个新元素(密码 = 文件中的密码,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);
【问题讨论】:
能否请您调试一下,并告诉我们 seg 在哪一行。发生故障了吗? 【参考方案1】:正如 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
中,不要忘记在释放list_element
空间之前释放passwort
空间:
while (current != NULL)
second = current->next;
free(current->password);
free(current);
current = second;
【讨论】:
好的。感谢您并抱歉在此处寻求解决方案之前没有使用调试器。不存在分段错误。但我仍然对 read_data 功能有疑问。程序无法正确读取密码。我将编辑我的问题并将新的 data_read funktion 放在那里。也许你可以帮我解决它。【参考方案2】:您的程序遇到的第一个问题是read_data()
没有为passwort
分配足够的空间。实际上,不清楚为什么要动态分配它,但鉴于您正在这样做,sizeof(passwort)
是一个指向 char 的指针的大小(因为这就是 passwort
的大小)——可能是 4 或 8 个字节.稍后,当您(尝试)将其内容复制到列表元素中时,您似乎假设分配的空间长度为 100 字节。为什么不简单地将其声明为 100 字符数组?
char passwort[100];
确实,如果您也以同样的方式声明list_element.passwort
,那么您在循环中的密码复制代码将是正确的,尽管有点不习惯。
事实上,正如@Derlin 所观察到的那样,该代码是有问题的。然而,他提出的解决方案是不正确的。你不能让列表元素指向本地的passwort
,只要它在整个例程中只分配一次。然后所有列表元素将具有相同的密码字符串,这不是您想要的。如果您希望您的列表元素像现在一样包含指向密码的指针,那么您需要将passwort
inside 的声明和分配移动到循环中,以便获得单独的密码为每个列表元素分配的空间。那么分配le->password = passwort
的建议是正确的。
另一个早期问题是您的insert_list()
函数严重损坏。
首先考虑当您尝试将一个元素插入一个空列表(由init_list()
初始化)时会发生什么。该列表的next
和last
成员都将为空,因此insert_list()
将尝试执行此代码:
mylist->last->next = le;
mylist->last = le;
mylist->last->next = NULL;
观察mylist->last
为空,因此第一行通过尝试取消引用空指针来调用未定义的行为。分段错误是一个非常合理的观察结果。您可以通过将第一行更改为
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;
【讨论】:
好的。谢谢你。很抱歉在此处寻求解决方案之前没有使用调试器。没有分段错误了,但是读取密码还是有问题。以上是关于C快速排序(链表)分段错误的主要内容,如果未能解决你的问题,请参考以下文章