尝试在C中使指针无效时的Segfault

所以现在我正在尝试实现一个deltaclock,我正在尝试的第一步是首先创建一个使用双链表的优先级队列(我稍后会做其他deltaclock的事情)。 从优先级队列中弹出的第一个元素是位于链表(或deltaClock结构)根目录的元素。

我在测试中将几个元素推送到队列中,并且在一些弹出操作之后,有一个案例,当它从几乎空的列表中弹出时会出现段错误。

当我在弹出方法中注释掉行中我说“clock-> root-> previous = 0;”时,当我弹出元素时,main方法中的程序不会出现段错误。 但是,由于前一个节点不再存在,因此需要删除指向前一个节点的指针。

当我执行pop操作时,如何才能使新root的前一个指针为null?

#include  #include  struct deltaNode{ int tics; // source struct deltaNode* next; struct deltaNode* previous; }; struct deltaClock{ struct deltaNode* root; struct deltaNode* tail; int size; }; void initDeltaClock(struct deltaClock **clock){ (*clock) = malloc(sizeof(struct deltaClock)); (*clock)->size = 0; } void push(struct deltaClock* clock, int numberOfTics){ if(clock->root == 0){ // root is empty, initialize it. clock->root = malloc(sizeof(struct deltaNode)); clock->root->tics = numberOfTics; clock->tail = clock->root; } else { struct deltaNode* newNode = malloc(sizeof(struct deltaNode)); newNode->tics = numberOfTics; struct deltaNode* temp = clock->root; if(newNode->tics tics){ clock->root->previous = newNode; newNode->next = clock->root; clock->root = newNode; } else { while(newNode->tics > temp->tics){ if(temp->next == 0){ break; } temp = temp->next; } if(temp->next == 0 && newNode->tics > temp->tics){ clock->tail->next = newNode; newNode->previous = clock->tail; clock->tail = newNode; } else{ temp->previous->next = newNode; newNode->previous = temp->previous; newNode->next = temp; temp->previous = newNode; } } } clock->size++; } int pop(struct deltaClock* clock){ struct deltaNode* temp = clock->root; if(temp == 0){ return -1; } int result = temp->tics; clock->root = clock->root->next; clock->root->previous = 0; free(temp); clock->size--; return result; } void printClock(struct deltaClock* clock){ struct deltaNode* iterator; iterator = clock->root; while(iterator != 0){ printf("\n%d",iterator->tics); iterator = iterator->next; } } int main(int argc, char* argv[]){ printf("\nHello world."); struct deltaClock* clock; initDeltaClock(&clock); push(clock, 3); push(clock, 2); push(clock, 7); push(clock, 33); push(clock, 221); push(clock, 5); printClock(clock); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); printf("\npopping %d",pop(clock)); push(clock, 25); printf("\npopping %d",pop(clock)); printf("\n"); } 

对于它的价值,这段代码运行得相当合理:

 #include  #include  struct deltaNode { int tics; // source struct deltaNode *next; struct deltaNode *previous; }; struct deltaClock { struct deltaNode *root; struct deltaNode *tail; int size; }; void initDeltaClock(struct deltaClock * *clock); void initDeltaClock(struct deltaClock * *clock) { (*clock) = malloc(sizeof(struct deltaClock)); (*clock)->size = 0; } void push(struct deltaClock *clock, int numberOfTics); void push(struct deltaClock *clock, int numberOfTics) { if (clock->root == 0) { // root is empty, initialize it. clock->root = malloc(sizeof(struct deltaNode)); clock->root->tics = numberOfTics; clock->tail = clock->root; } else { struct deltaNode *newNode = malloc(sizeof(struct deltaNode)); newNode->tics = numberOfTics; struct deltaNode *temp = clock->root; if (newNode->tics < temp->tics) { clock->root->previous = newNode; newNode->next = clock->root; clock->root = newNode; } else { while (newNode->tics > temp->tics) { if (temp->next == 0) break; temp = temp->next; } if (temp->next == 0 && newNode->tics > temp->tics) { clock->tail->next = newNode; newNode->previous = clock->tail; clock->tail = newNode; } else { temp->previous->next = newNode; newNode->previous = temp->previous; newNode->next = temp; temp->previous = newNode; } } } clock->size++; } int pop(struct deltaClock *clock); int pop(struct deltaClock *clock) { struct deltaNode *temp = clock->root; if (temp == 0) return -1; int result = temp->tics; clock->root = clock->root->next; if (clock->root != 0) clock->root->previous = 0; free(temp); clock->size--; return result; } void printClock(struct deltaClock *clock); void printClock(struct deltaClock *clock) { struct deltaNode *iterator; iterator = clock->root; while (iterator != 0) { printf(" %d", iterator->tics); iterator = iterator->next; } putchar('\n'); } int main(void) { printf("Hello world.\n"); struct deltaClock *clock; initDeltaClock(&clock); push(clock, 3); push(clock, 2); push(clock, 7); push(clock, 33); push(clock, 221); push(clock, 5); printClock(clock); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); printf("popping %d\n", pop(clock)); push(clock, 25); printf("popping %d\n", pop(clock)); printf("\n"); } 

它产生:

 Hello world. 2 3 5 7 33 221 popping 2 popping 3 popping 5 popping 7 popping 33 popping 221 popping -1 popping -1 popping -1 popping -1 popping -1 popping -1 popping 25 

主要变化不是设置clock->root->previous = 0; 除非clock->root不为null。

次要更改是在print语句的末尾添加换行符,并从头开始删除它们。 并且列表是水平打印的,每行多个数字,而不是每行一个数字。

显示的代码使用以下内容进行干净编译:

 gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ -Wold-style-definition pl.c -o pl 

使用这些选项干净地编译需要函数声明; 总的来说,这是一个好主意。 另一种方法是使函数全部为静态,这也适用于单文件程序。 使代码编译干净所需的唯一更改是添加函数原型并将main(int argc, char **argv)替换为main(void) – 做得好,这非常好。

除了给出的其他建议外,在处理指针时,总是将它们初始化为NULL或最终值是个好主意,不要让它们悬空。

例如,在initDeltaClock(*clock)->root(*clock)->tail未初始化。 这意味着在第一次push调用中,即使检查if (clock->root == 0)也无效,因为clock->root尚未初始化为0。

这是一个建议的改进:

 void initDeltaClock(struct deltaClock **clock){ (*clock) = calloc(sizeof(struct deltaClock)); /* <-- zero out entire contents after alloc */ } 

这同样适用于在mall中使用malloc的结构,您可以使用calloc而不是malloc来确保返回的块被清除为零,或者您应该将结构的指针显式设置为零。

此外,如果这不仅仅是玩具项目,请始终检查分配函数的返回值以检查内存不足错误。