//结构体
typedef struct node{
int elem;
int index;
struct node* next;
}Node,* ListNode;
//打印链表
void print(ListNode root){
ListNode temp = root->next;
while (temp) {
printf("index-%d elem-%d\n", temp->index, temp->elem);
temp = temp->next;
}
}
//链表初始化
void initList(ListNode head, int size) {
ListNode end;
end = (ListNode)malloc(sizeof(Node));
end = head;
for (int i = 0; i < size; i++) {
ListNode temp;
temp = (ListNode)malloc(sizeof(Node));
scanf("%d", &temp->elem);
temp->index = i;
end->next = temp;
end = temp;
}
end->next = NULL;
}
int main() {
ListNode root;
root = (ListNode)malloc(sizeof(Node));
root->elem = -1;
root->index = -1;
initList(root, 3);
print(root);
return 0;
}
问题描述
首先这是我已经改过正确的代码,代码的初衷是我想在主函数当中定义一个链表头root,并将他的地址传给函数initList进行初始化,因为我是直接将地址传给了函数,所以我不需要再通过返回值把函数中初始化完成的链表头节点通过return回主函数,便可直接去打印root链表。而不需要再root = initList(root,3);
这样去接收初始化链表的头节点。
理论依据
首先我想去这样做,是因为c语言可以实现地址间的相互传递,及指针,从而规避形参与实参之间的问题,使得函数可以直接在主函数所定义的变量中进行更改,多应用于数组的操作,在主函数中将定义的数组地址传给函数,函数可以直接对数组进行各种操作,而不再需要重新定义形参数组,进而节省空间。
BUG出现
很不幸,想法很完美,可就是结果很悲惨,在确定函数中的执行流程没有任何问题的情况下,主函数中所定义的root节点,却总是和原先定义的一样,并未被初始化,这一度让我以为函数任然背着我悄悄的创建了形参,而绕开了我所传递的root根节点。
问题所在
经过长时间,大规模的不懈寻找,我终于知道了问题的关键所在,全都是因为这几行代码:
root = (ListNode)malloc(sizeof(Node));
root->elem = -1;
root->index = -1;
原先的代码,我是把他们放在了函数中去执行,虽然功能完全一样,但是含义却发生了改变,理论上是没有问题的,传给函数一个NULL的头节点,函数要先给这个节点初始化,然后再去尾插法初始后续节点,但是!但是!但是! 当我在主函数定义root时,他就已经被分配了地址,虽然初始化为NULL,但是他已经有了属于它的地址我们假设这个地址为A,在进入函数之后,在我们再次初始化头节点的时候,root = (ListNode)malloc(sizeof(Node));
我们再一次给头节点寻找了空间,我们再次假设这个地址为B,后续所有的初始化都是在以B为头节点的链表上进行操作,所以当我们完成整个初始化时,以A为地址的头节点root依旧时空,而这里以B为头节点的链表就相当于函数自己创建的形参一样被程序所“遗忘”,这就是为什么我们传递root地址给print函数打印时,会报错为空的原因。
解决问题
找到了问题的关键,我们便知道了该如何去解决他,仅仅是把分配头节点地址的几行代码提至主函数完成即可,寻找好了地址A,再将其传给函数,让函数仅仅只完成后续初始化操作即可!!!!(具体操作如文章开头所示代码)
感悟
虽然是一个很小很小的问题,但是他让我思索了很久,写代码往往总是那些不起眼的小问题才可能会让你代码全盘崩掉,因为你认为他不会错,你就主观上放弃了对他的检查和思考,再其次就是代码的顺序真的!!!真的!!!真的!!!非常重要,这也就是为社么操作系统或者数据库设计不好,就可能出现死锁,即使每条代码都没有问题,算法也是最优的,但是b代码比a代码先执行了就是可能会出现问题,编程还是要客观分析,以机器的思维去审视代码,少一些主观臆断。