野指针
28:什么是野指针?
野指针是什么?有什么作用?
野指针不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,但是野指针是很难判断出,是很危险的,而且if语句对它不起作用。野指针的成员主要有两种:
1:指针变量没初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,所以指针变量在创建的同时应当初始化,要么将指针设置为NULL,要么指向合法的内存。
2:指针p被free或delete后,没有置为NULL。
29:代码分析查错
下面的程序有什么bug?
1 #include "stdafx.h" 2 using namespace std; 3 4 int main() 5 { 6 short*bufptr; 7 short bufarray[20]; 8 short var = 0x20; 9 *bufptr = var; 10 bufarray[0] = var; 11 12 return 0; 13 getchar(); 14 }
第6行正确,声明了一个short*类型的指针,但没有对它进行初始化。
第7行正确,声明一个含有20个元素的数组。每个元素都是short类型。
第8行正确,声明short类型的变量var,并把它初始化为0x20。
第9行错误。因为bufptr没有被初始化,是个野指针,因此将bufptr指针指向的内容赋值为var变量的值就变得十分危险,会导致程序崩溃。为了杜绝这种错误,可以对bufptr进行初始化。将第6行修改为:
short*bufptr=(short *)malloc(sizeof(short));
代码10正确。把变量var值赋值给bufarray的第一个元素。
30:有了malloc和free,为什么还要new和delete
malloc与free和new与delete的区别
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可以用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,仅用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,因此不能把执行构造函数和析构函数的任务强加于malloc/free函数。
C++语言需要能完成动态内存分配和初始化工作的运算符,即new运算符,以及能完成清理和释放内存工作的运算符,即delete运算符。注意new/delete不是库函数。
1 #include "stdafx.h" 2 #include <iostream> 3 using namespace std; 4 5 class Obj 6 { 7 public: 8 Obj(void) 9 { 10 cout << "Initialization" << endl; 11 } 12 ~Obj(void) 13 { 14 cout << "Destroy" << endl; 15 } 16 }; 17 18 void UseMallocFree(void) 19 { 20 cout << "in UseMallocFree()..." << endl; 21 Obj*a = (Obj*)malloc(sizeof(Obj)); 22 free(a); 23 } 24 25 void UseNewDelete(void) 26 { 27 cout << "in UseNewDelete()..." << endl; 28 Obj*a = new Obj; 29 delete a; 30 } 31 32 33 int main() 34 { 35 UseMallocFree(); 36 UseNewDelete(); 37 getchar(); 38 return 0; 39 }
对于非内部数据类型的对象而言,对象在消亡之前要自动执行析构函数。由于malloc/free函数是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加在malloc/free函数之上,因此只能使用new/delete运算符
31:程序改错--指针的初始化
野指针必须初始化为NULL
1 #include "stdafx.h" 2 #include <stdio.h> 3 #include <malloc.h> 4 5 6 struct Tag_Node 7 { 8 struct Tag_Node* left; 9 struct Tag_Node* right; 10 int value; 11 }; 12 13 typedef struct Tag_Node TNode; 14 15 TNode* root = NULL; 16 17 void append(int N); 18 19 20 int main() 21 { 22 append(63); 23 append(45); 24 append(32); 25 append(77); 26 append(96); 27 append(21); 28 append(17); 29 30 return 0; 31 } 32 33 void append(int N) 34 { 35 TNode* NewNode = (TNode*)malloc(sizeof(TNode)); 36 NewNode->value = N; 37 38 if (root==NULL) 39 { 40 root = NewNode; 41 return; 42 } 43 else 44 { 45 TNode* temp; 46 temp = root; 47 while ((N>=temp->value&&temp->left!=NULL)|| 48 (N<temp->value&&temp->right!=NULL)) 49 { 50 while (N>=temp->value&&temp->left!=NULL) 51 temp = temp->left; 52 while (N < temp->value&&temp->right!=NULL) 53 temp = temp->right; 54 55 } 56 if (N>=temp->value) 57 { 58 temp->left = NewNode; 59 } 60 else 61 62 temp->right = NewNode; 63 return; 64 } 65 }
TNode是结构体类型,它有left和right两个成员指针,分别代表链接左右两个元素,value成员表示元素节点的数据。在append函数中,数据从左到右按降序排列。因此在代码第47,48,50行中使用while循坏来查找合适的位置。在这4行都采用temp的指针left或指针right与NULL进行判断,然而对堆中分配的内存只做成员value的初始化(代码36行),没有把left和right初始化为NULL,因此指针left和指针right与NULL进行判断不起作用。从而程序会对野指针指向的地址进行赋值,从而导致程序崩溃。
改进后的代码:
1 #include <stdio.h> 2 #include <malloc.h> 3 4 5 struct Tag_Node 6 { 7 struct Tag_Node* left; 8 struct Tag_Node* right; 9 int value; 10 }; 11 12 typedef struct Tag_Node TNode; 13 14 TNode* root = NULL; 15 16 void append(int N); 17 void print(); 18 19 20 int main() 21 { 22 append(63); 23 append(45); 24 append(32); 25 append(77); 26 append(96); 27 append(21); 28 append(17); 29 printf("head:%d\n", root->value); 30 print(); //打印链表所有元素 31 32 } 33 34 void append(int N) 35 { 36 TNode* NewNode = (TNode*)malloc(sizeof(TNode)); 37 NewNode->value = N; 38 NewNode->left = NULL; //初始化left 39 NewNode->right = NULL; //初始化right 40 41 42 if (root==NULL) 43 { 44 root = NewNode; 45 return; 46 } 47 else 48 { 49 TNode* temp; 50 temp = root; 51 while ((N>=temp->value&&temp->left!=NULL)|| 52 (N<temp->value&&temp->right!=NULL)) 53 { 54 while (N>=temp->value&&temp->left!=NULL) 55 temp = temp->left; 56 while (N < temp->value&&temp->right!=NULL) 57 temp = temp->right; 58 59 } 60 if (N>=temp->value) 61 { 62 temp->left = NewNode; 63 NewNode->right = temp; //形成双向链表 64 } 65 else 66 { 67 temp->right = NewNode; 68 NewNode->left = temp; //形成双向链表 69 } 70 71 return; 72 } 73 } 74 75 void print() 76 { 77 TNode* leftside = NULL; 78 79 if (root=NULL) 80 { 81 printf("There is not any element"); 82 return; 83 } 84 leftside = root->left; 85 86 while (1) 87 { 88 if (leftside->left == NULL) 89 { 90 break; 91 } 92 leftside = leftside->left; 93 } 94 while (leftside!=NULL) 95 { 96 printf("%d", leftside->value); 97 leftside = leftside->right; 98 } 99 }
在代码38和39中添加了成员指针left和right的初始化。这样就杜绝了野指针的产生。在代码63和代码68的目的是为了使链表尾双向链表。这样在遍历链表时就比较方便。print函数从左到右打印链表中所有元素的value成员。执行结果:
head:63
96 77 63 45 32 21 17
可以看出,root节点是第一个插入到链接的节点,其数据值为63.链表按照从左到右降序排列
没有对新增加的节点成员指针left和right作初始化,它们都是野指针,在随后与NULL比较时不起判断的作用。最终由于对野指针指向的内存块赋值导致程序崩溃。
浙公网安备 33010602011771号