先说结论:要是对null指针像对普通结构体指针那样子用->取结构体中的值(后简称对null指针取值),编译能过但是会导致程序运行到这一句的时候停在这里,程序后面的代码不会跑到也不会有任何相应的输出,程序运行也不会显示结束,debug的时候会出现错误提示:读取访问权限冲突。(指针名) 是 nullptr。
原因:NULL指针是指该指针不指向任何有效的存储单元,对NULL指针“指向的存储单元取值”自然就是程序不能做到的事情(对比野指针是可以做到这一点的),程序自己不能从代码中找到解决方法不能往前就只能停在这里了。
解决办法是:1、避免出现对null指针取值这样的情况;2、当出现null指针的时候单独进行判断处理
今天在做谭浩强C语言第四版的课后习题时,做到一题“现有a、b两个链表已经存放学生信息(学号、成绩),把两个链表合并并按学号升序排列”,我写完看答案的时候发现答案是在假设原来的a、b链表都是升序排列的情况下把b链的结点插入a链中,我觉得这个想法会比我原来单纯把两个链先简单粗暴连起来再用常见单链的排序方法进行排序的时间复杂度更低,因此我也尝试按照这个思路再写一遍。
但是等我实际再写一遍的时候发现悲剧了,不仅要考虑的情况很多(a链和b链的各结点之间的关系多种多样),写起来也很容易出错。我终于写出了在多种情况下都能输出正确答案的代码,但是唯独在a链第一个结点的学号就比b链的所有结点的学号都大的时候程序没有任何输出。
//省略头命令声明部分
struct stu
{
int num;
int scr;
struct stu *next;
};
int main()
{
struct stu *creat();
struct stu *arr(struct stu *ah, struct stu *bh);
struct stu *ahead, *bhead,*head,*p;
ahead = creat();
bhead = creat();
//head = arr(ahead, bhead);
struct stu *pa1, *pa2, *pb1, *pb2;
pa1 = pa2 = ahead;
pb1 = pb2 = bhead;
while (pa1 != NULL && pb1 != NULL)
{
while ((pb1->num > pa1->num) && (pa1->next != NULL))
{
pa2 = pa1;
pa1 = pa1->next;
}
if (pb1->num < pa1->num)
{
if (ahead == pa1)//即pb->num<ah->num
{
ahead = pb1;
}
else
{
pa2->next = pb1;
}
pb2 = pb2->next;
pb1->next = pa1;
pa2 = pb1;
pb1 = pb2;
}
if ((pa1->next == NULL) && (pb1->num >= pa1->num))
{
pa2 = pa1;
pa1 = pa1->next;
}/*没有pb1!=NULL则当pb1==null时回到大while循环进行判断,不满足条件,程序离开大while
pb1->num>=pa1->num:要保证b链未遍历部分确实是学号都>=a链最后一个结点的学号;
问题:若pb1==null则能过编译,但是进行上面的if判断时会导致程序没有输出(把pb1=pb2放到此循环后或在条件里面加上pb1 !=null就正常输出)
为什么:引发了未经处理的异常:读取访问权限冲突。pb1 是 nullptr。*/
}
if (pa1 == NULL && pb1 != NULL)pa2->next = pb1;
p = ahead;
while (p != NULL)
{
printf("%d,%d\n", p->num, p->scr);
p = p->next;
}
return 0;
}
struct stu *creat()
{
struct stu *xin = malloc(sizeof(struct stu)),*head,*p0;
int n = 0;
printf("请输入学生数据(学号、成绩):\n");
scanf("%d%d", &xin->num, &xin->scr);
p0 = xin;
while (xin->num != 0)
{
n++;
if (n == 1)head = xin;
else
{
p0->next = xin;
}
p0 = xin;
xin = malloc(sizeof(struct stu));
scanf("%d%d", &xin->num, &xin->scr);
}
p0->next = NULL;
return head;
}
最终我的解决思路及办法就是:判断当前的指针的值是不是NULL,如果是的话避免对该指针取值的情况出现,如果该指针为NULL时需要作为判断条件之一,改用其他方式在其他地方对该指针为NULL时进行判断。就像我在上面程序注释问题后面的括号里写的:在if判断条件里面加上pb1!=NULL的条件限制,或者把pb1的赋值语句放在if语句之后,两种办法都是避免出现对NULL指针取值并进行判断的情况出现。改过之后的程序(部分)如下:
....
if ((pb1 != NULL) && (pa1->next == NULL) && (pb1->num >= pa1->num))//增加条件
{
pa2 = pa1;
pa1 = pa1->next;
}
//或
....
pb2 = pb2->next;
pb1->next = pa1;
pa2 = pb1;
}
if ((pa1->next == NULL) && (pb1->num >= pa1->num))
{
pa2 = pa1;
pa1 = pa1->next;
}
pb1=pb2;//赋值后放