user-0 提问: 请大家看看我这几个函数中存在内存溢出、数组越界的情况吗

回复:
user: user-1
content: 数组越界，野指针用gflags测测看。

user: user-0
content: 引用 12 楼 ALNG 的回复:”而且你的插入步骤我觉得还要加一个prev->pnext = q;该函数最后一句*p=q是为什么，为什么要修改它呢？我觉得如果是插入第0个节点改变还行，那插入中间节点为什么也要，这句不懂 ===========================================  prev只是来记录指针值的，prev可能是NULL, 所以没有用prev->pnext=q. 真正完成prev->pnext=q这个逻辑的正是*p=q. 如果*p这个指针或者是head，或者是要插在其后的节点的pnext指针。功能上肯定没有问题的，测试过了，不会是碰巧。你的代码能通过测试，相信也没有问题，问题出在别处。但你的插入的实现真心效率稍差，你的插入可能遍历链表两遍，有些代价是不必要的。  其实我上面也有点玩花，没必要的让程序难解。后来想了以下，其实只要区分两种情况，一种是插在最前面，另外一种是插入现有的某个节点的后面，如果按这种思路来写插入操作就容易写也容易懂了。下面的随手敲的，主要是意思   void insert_node(node **list, int i, int value) {     node * a; // 要插在其后的节点的指针     node * n; // 新节点指针     if(i==0) // 插在最前面     {          n=(node*)malloc(sizeof(node));          n->data=value;          n->plast=NULL;  // 设置新插节点的前后节点指针值          n->pnext=list;          if(*list) // 设置其后续节点的plast             (*list)->plast=n;          *list=n;     }     a=*list;     while(--i>0 && a!=NULL)        a=a->next;     if(i!=0 || a==NULL) //越界        exit(1);     n=(node*)malloc(sizeof(node));     n->data=value;     n->plast=a;  // 设置新插节点的前后节点指针值     n->pnext=a->pnext;     if(a->pnext)       a->pnext->plast=n;     a->pnext=n;  //一共改四个指针(或三个，如果a是原链表的最后一节)      }   从这个naive实现的基础上，设法合并让malloc, 和设置指针们的操作只进行一次。  void insert_node(node **list, int i, int value) {     // 重复利用list为要修改的指向新节点的指针的指针      // 这行没必要了，用上面的list来完成  //node * a; // 要插在其后的节点的指针     node * n; // 新节点指针     node * plast; // 新节点的前驱结点指针应赋的值     if(i==0) // 插在最前面     {            // list的值已经是对的了，不需调整                   //n=(node*)malloc(sizeof(node));          //n->data=value;          plast=NULL;  // 设置新插节点的前后节点指针值          n->pnext=list;          // 下面三行合并到后面，不需要修改          //if(*list) // 设置其后续节点的plast           //   (*list)->plast=n;          //*list=n;     }else{         a=*list;         while(--i>0 && a!=NULL)            a=a->next;         if(i!=0 || a==NULL) //越界            exit(1);          要修改a->pnext, 所以让list为a->pnext的地址          list=&a->pnext;          plast=a;     }      // 好了，两种情况下的插入都可以共用下面的代码了     n=(node*)malloc(sizeof(node));     n->data=value;     n->plast=plast;  // 设置新插节点的前后节点指针值     n->pnext=plast?plast->next:NULL;     if(plast && plast->pnext)        plast->pnext->plast=n;     *list=n;  //一共改四个指针(或三个，如果a是原链表的最后一节) }   上面的    if(plast && plast->pnext)        plast->pnext->plast=n; 还显得有点笨拙，如果用个变量记住后续节点，就基本上是我原来的版本了。看别人完成的代码往往比较痛苦。 思路很新颖，很有想法。 这几天我也对比了几处代码，我的错误可能是我理解题有偏差，插入函数

user: user-0
content: 引用 8 楼 ALNG 的回复:我上面在printnode里面有检测代码的，每次插入、删除后看看链表是否合法。  你可以把这个函数放到你的代码里面，然后    int main() {      struct strlnode* num;       insertnode(&num 0, 5);// 头部插入      printnode(num);      insertnode(&num 0, 6);//头部插入第二次      printnode(num);      insertnode(&num 2, 6); //尾部插入      printnode(num);      insertnode(&num 3, 8); //尾部插入第二次      printnode(num);      insertnode(&num 2, 0); //中间插入      printnode(num);                }   运行看看是什么环节出了问题。也用这个测试我的代码，插入和删除节点都试试，看看错的错在哪里，对的对在哪里，比较一下找出原因，就达到学习效果了。 你这个我试过，你的测试代码直接插入，并没有用create函数创建双链表，直接用此段代码会出错，因为num声明没有初始化为null，故检查不出空，报错了；后来我初始化为空后可以，在链表首尾中间查也没问题，大整数相加也没问题的

user: user-2
content: ”而且你的插入步骤我觉得还要加一个prev->pnext = q;该函数最后一句*p=q是为什么，为什么要修改它呢？我觉得如果是插入第0个节点改变还行，那插入中间节点为什么也要，这句不懂 ===========================================  prev只是来记录指针值的，prev可能是NULL, 所以没有用prev->pnext=q. 真正完成prev->pnext=q这个逻辑的正是*p=q. 如果*p这个指针或者是head，或者是要插在其后的节点的pnext指针。功能上肯定没有问题的，测试过了，不会是碰巧。你的代码能通过测试，相信也没有问题，问题出在别处。但你的插入的实现真心效率稍差，你的插入可能遍历链表两遍，有些代价是不必要的。  其实我上面也有点玩花，没必要的让程序难解。后来想了以下，其实只要区分两种情况，一种是插在最前面，另外一种是插入现有的某个节点的后面，如果按这种思路来写插入操作就容易写也容易懂了。下面的随手敲的，主要是意思   void insert_node(node **list, int i, int value) {     node * a; // 要插在其后的节点的指针     node * n; // 新节点指针     if(i==0) // 插在最前面     {          n=(node*)malloc(sizeof(node));          n->data=value;          n->plast=NULL;  // 设置新插节点的前后节点指针值          n->pnext=list;          if(*list) // 设置其后续节点的plast             (*list)->plast=n;          *list=n;     }     a=*list;     while(--i>0 && a!=NULL)        a=a->next;     if(i!=0 || a==NULL) //越界        exit(1);     n=(node*)malloc(sizeof(node));     n->data=value;     n->plast=a;  // 设置新插节点的前后节点指针值     n->pnext=a->pnext;     if(a->pnext)       a->pnext->plast=n;     a->pnext=n;  //一共改四个指针(或三个，如果a是原链表的最后一节)      }   从这个naive实现的基础上，设法合并让malloc, 和设置指针们的操作只进行一次。  void insert_node(node **list, int i, int value) {     // 重复利用list为要修改的指向新节点的指针的指针      // 这行没必要了，用上面的list来完成  //node * a; // 要插在其后的节点的指针     node * n; // 新节点指针     node * plast; // 新节点的前驱结点指针应赋的值     if(i==0) // 插在最前面     {            // list的值已经是对的了，不需调整                   //n=(node*)malloc(sizeof(node));          //n->data=value;          plast=NULL;  // 设置新插节点的前后节点指针值          n->pnext=list;          // 下面三行合并到后面，不需要修改          //if(*list) // 设置其后续节点的plast           //   (*list)->plast=n;          //*list=n;     }else{         a=*list;         while(--i>0 && a!=NULL)            a=a->next;         if(i!=0 || a==NULL) //越界            exit(1);          要修改a->pnext, 所以让list为a->pnext的地址          list=&a->pnext;          plast=a;     }      // 好了，两种情况下的插入都可以共用下面的代码了     n=(node*)malloc(sizeof(node));     n->data=value;     n->plast=plast;  // 设置新插节点的前后节点指针值     n->pnext=plast?plast->next:NULL;     if(plast && plast->pnext)        plast->pnext->plast=n;     *list=n;  //一共改四个指针(或三个，如果a是原链表的最后一节) }   上面的    if(plast && plast->pnext)        plast->pnext->plast=n; 还显得有点笨拙，如果用个变量记住后续节点，就基本上是我原来的版本了。看别人完成的代码往往比较痛苦。

user: user-2
content: 那就不是你的插入，删除，相加环节的错误。每次使用后有否释放动态分配的内存，有没有可能内存溢出？  网址给出来。

user: user-2
content: 写代码一定要学会设计测试。需要点时间，慢慢的你就都会了。

user: user-2
content: 我上面在printnode里面有检测代码的，每次插入、删除后看看链表是否合法。  你可以把这个函数放到你的代码里面，然后    int main() {      struct strlnode* num;       insertnode(&num 0, 5);// 头部插入      printnode(num);      insertnode(&num 0, 6);//头部插入第二次      printnode(num);      insertnode(&num 2, 6); //尾部插入      printnode(num);      insertnode(&num 3, 8); //尾部插入第二次      printnode(num);      insertnode(&num 2, 0); //中间插入      printnode(num);                }   运行看看是什么环节出了问题。也用这个测试我的代码，插入和删除节点都试试，看看错的错在哪里，对的对在哪里，比较一下找出原因，就达到学习效果了。

user: user-0
content: 引用 4 楼 zhao4zhong1 的回复:试试0+0=? 还真没试过这个，试试看

user: user-0
content: 引用 2 楼 ALNG 的回复:吐血。以下代码仅供学习链表的插入删除。大整数的计算不带这么玩的，太低效了。你根本没有在任意位置插入一个数位的要求，为什么不用一个哪怕字符数组呢。这是另一个题目了，anyway:   #include <stdio.h> #include <stdlib.h>  struct strlnode {     int data;     struct strlnode *plast;     struct strlnode *pnext; };  // 不如传入strlnode* // int getnodenum(struct strlnode *p)  /*获取链表中节点个数*/ {     int nodenum = 0;     while(p != NULL)     {         nodenum++;         p = p->pnext;     }     return nodenum; }  void printnode(struct strlnode*p) { #ifdef _DEBUG 	if(p && p->plast!=NULL) 		printf("head node's plast not set to NULL\n"); #endif	 	while(p) 	{ 		printf("%d ", p->data); #ifdef _DEBUG 		if(p->pnext && p->pnext->plast!=p) 			printf("\nNode(data %d)'s next node's previous node not correctly set\n", p->data); #endif 		p=p->pnext; 	} 	putchar('\n'); }  /* 传入strlnode**的原因就是为了修改它，如果没有修改，结果必然是错的  */ void insertnode(struct strlnode **p, int i, int x) /* 在链表第i个位置插入数据等于x的节点 */ { 	struct strlnode *q, *prev=NULL;     if(i < 0)         exit(1);     while(i!=0)     {         --i;         if(*p==NULL)//越界             exit(-1);         prev=*p;         p=&(*p)->pnext;     }     q=(struct strlnode*)malloc(sizeof(*q));         q->data=x;     q->pnext=*p;     q->plast=prev;     if(*p)         (*p)->plast=q;     *p=q; }  void deletenode(struct strlnode **p, int i) /* 删除链表中第i个节点 */ { 	/* locate the node to delete. we can safely assume p is not NULL because of the way we use it */ 	struct strlnode* prev=NULL, *q; 	while(i!=0) 	{ 		--i; 		if(*p==NULL) 			exit(-1); // out of bound 		prev=*p; 		p=&(*p)->pnext; 	} 	if(*p==NULL) 		exit(-1); // out of bound 	q=*p; 	*p=q->pnext; 	if(q->pnext) 		q->pnext->plast=prev; 	free(q); }  void bignumberplus(struct strlnode **plus, struct strlnode **p, struct strlnode **q) /* 使用链表实现大整数相加 */ {      struct strlnode *p0,*q0,*plus0;      unsigned int add,flag=0;      p0 = *p;      q0 = *q;            while(p0->pnext != NULL)          p0 = p0->pnext;      while(q0->pnext != NULL)          q0 = q0->pnext;            while(q0 != NULL || p0 != NULL)      {          add=flag;          if(q0!=NULL)          {              add+=q0->data;          	 q0=q0->plast;          }          if(p0!=NULL)          {              add+=p0->data;              p0=p0->plast;          }         flag=0;         if(add>=10)         {             ++flag;             add-=10;         }         insertnode(plus,0,add);      }      if(flag)          insertnode(plus,0,1);  }   int main() { 	struct strlnode * head=NULL; 	struct strlnode * num2=NULL, *sum=NULL;  	insertnode(&head, 0, 3); 	insertnode(&head, 0, 4); 	insertnode(&head, 0, 5); 	insertnode(&head, 0, 7); 	insertnode(&head, 0, 8); 	insertnode(&head, 0, 1); 	printnode(head);  	insertnode(&num2, 0,1); 	insertnode(&num2, 0, 5); 	insertnode(&num2, 0, 2); 	insertnode(&num2, 0, 8); 	insertnode(&num2, 0, 7); 	insertnode(&num2, 0, 3); 	printnode(num2); 	 	bignumberplus(&sum, &head, &num2); 	printnode(sum); }  我这只是做题的，实际用这确实是有点浪费~~~ 链表插入函数，i你没有判断大于链表节点个数是因为你在while循环里面加了为空节点的判断的，但是我把链表节点数判断加进去了，在while里面循环液不可能溢出啊，因为我的i肯定取值范围是“0=<i<=链表节点数-1”而且你的插入步骤我觉得还要加一个prev->pnext = q;该函数最后一句*p=q是为什么，为什么要修改它呢？我觉得如果是插入第0个节点改变还行，那插入中间节点为什么也要，这句不懂 删除节点函数：我觉得也是把i的取值范围判断在我的节点范围内就行了，所以就不会有空或者内存溢出的情况，那我的难道也不行吗？ 赞一个你最后的一个大整数相加写得真好，代码函数变小了好多！！！ 对于我上面的问题，还希望你耐心解答，谢谢！

user: user-0
content: 引用 3 楼 truelance 的回复:"VC能通过" 你只测了一两个用例吧 要学会写测试用例, 你这么多代码, 要做单元测试的话, 至少应该有30个用例. 测试用例？这个怎么弄?能否介绍下，我觉得这是个突破口

user: user-3
content: 试试0+0=?

user: user-4
content: "VC能通过" 你只测了一两个用例吧 要学会写测试用例, 你这么多代码, 要做单元测试的话, 至少应该有30个用例.

user: user-2
content: 吐血。以下代码仅供学习链表的插入删除。大整数的计算不带这么玩的，太低效了。你根本没有在任意位置插入一个数位的要求，为什么不用一个哪怕字符数组呢。这是另一个题目了，anyway:   #include <stdio.h> #include <stdlib.h>  struct strlnode {     int data;     struct strlnode *plast;     struct strlnode *pnext; };  // 不如传入strlnode* // int getnodenum(struct strlnode *p)  /*获取链表中节点个数*/ {     int nodenum = 0;     while(p != NULL)     {         nodenum++;         p = p->pnext;     }     return nodenum; }  void printnode(struct strlnode*p) { #ifdef _DEBUG 	if(p && p->plast!=NULL) 		printf("head node's plast not set to NULL\n"); #endif	 	while(p) 	{ 		printf("%d ", p->data); #ifdef _DEBUG 		if(p->pnext && p->pnext->plast!=p) 			printf("\nNode(data %d)'s next node's previous node not correctly set\n", p->data); #endif 		p=p->pnext; 	} 	putchar('\n'); }  /* 传入strlnode**的原因就是为了修改它，如果没有修改，结果必然是错的  */ void insertnode(struct strlnode **p, int i, int x) /* 在链表第i个位置插入数据等于x的节点 */ { 	struct strlnode *q, *prev=NULL;     if(i < 0)         exit(1);     while(i!=0)     {         --i;         if(*p==NULL)//越界             exit(-1);         prev=*p;         p=&(*p)->pnext;     }     q=(struct strlnode*)malloc(sizeof(*q));         q->data=x;     q->pnext=*p;     q->plast=prev;     if(*p)         (*p)->plast=q;     *p=q; }  void deletenode(struct strlnode **p, int i) /* 删除链表中第i个节点 */ { 	/* locate the node to delete. we can safely assume p is not NULL because of the way we use it */ 	struct strlnode* prev=NULL, *q; 	while(i!=0) 	{ 		--i; 		if(*p==NULL) 			exit(-1); // out of bound 		prev=*p; 		p=&(*p)->pnext; 	} 	if(*p==NULL) 		exit(-1); // out of bound 	q=*p; 	*p=q->pnext; 	if(q->pnext) 		q->pnext->plast=prev; 	free(q); }  void bignumberplus(struct strlnode **plus, struct strlnode **p, struct strlnode **q) /* 使用链表实现大整数相加 */ {      struct strlnode *p0,*q0,*plus0;      unsigned int add,flag=0;      p0 = *p;      q0 = *q;            while(p0->pnext != NULL)          p0 = p0->pnext;      while(q0->pnext != NULL)          q0 = q0->pnext;            while(q0 != NULL || p0 != NULL)      {          add=flag;          if(q0!=NULL)          {              add+=q0->data;          	 q0=q0->plast;          }          if(p0!=NULL)          {              add+=p0->data;              p0=p0->plast;          }         flag=0;         if(add>=10)         {             ++flag;             add-=10;         }         insertnode(plus,0,add);      }      if(flag)          insertnode(plus,0,1);  }   int main() { 	struct strlnode * head=NULL; 	struct strlnode * num2=NULL, *sum=NULL;  	insertnode(&head, 0, 3); 	insertnode(&head, 0, 4); 	insertnode(&head, 0, 5); 	insertnode(&head, 0, 7); 	insertnode(&head, 0, 8); 	insertnode(&head, 0, 1); 	printnode(head);  	insertnode(&num2, 0,1); 	insertnode(&num2, 0, 5); 	insertnode(&num2, 0, 2); 	insertnode(&num2, 0, 8); 	insertnode(&num2, 0, 7); 	insertnode(&num2, 0, 3); 	printnode(num2); 	 	bignumberplus(&sum, &head, &num2); 	printnode(sum); }

user: user-2
content: // 传入strlnode**, 就需要检查p为NULL指针的情形, 不如传入strlnode* // int getnodenum(struct strlnode *p)  /*获取链表中节点个数*/ {     int nodenum = 0; //    struct strlnode *q; //    if( p ) //    { //        q=*p 	    while(p != null) 	    { 	        nodenum++; 	        p = p->pnext; 	    } //    }     return nodenum; }  // 同样要检查p为NULL指针的情形， void insertnode(struct strlnode **p, int i, int x) /* 在链表第i个位置插入数据等于x的节点 */ {     struct strlnode *q=null,*q0=null;     q = *p;     if(i < 0 || i > (getnodenum(p) - 1))         exit(1);     else     {         q0 = (struct strlnode *)malloc(sizeof(struct strlnode));         q0->data = x;         if(0 == i)         {             q0->pnext = q;             q->plast = q0;             q0->plast = null;             *p = q0;         }else         {             while(i > 0)             {                 i--;                 q = q->pnext;             }             q->plast->pnext = q0;             q0->plast = q->plast;             q0->pnext = q;             q->plast = q0;         }     }     }  // 同样要检查 void deletenode(struct strlnode **p, int i) /* 删除链表中第i个节点 */ {     struct strlnode *q = *p;     if(i < 0 || i > getnodenum(p) - 1)         exit(1);     else     {                 while(i > 0)                 {             i--;             q = q ->pnext;         }         if(q->plast == null)        //del first         {             *p = q ->pnext;             free(q);             q = *p;             q ->plast = null;         }else if(q->pnext == null)        //del last         {                       q->plast->pnext = null;             free(q);         }else         {             q->plast->pnext = q->pnext;             q->pnext->plast = q->plast;             free(q);         }     } } void bignumberplus(struct strlnode **plus, struct strlnode **p, struct strlnode **q) /* 使用链表实现大整数相加 */ {      struct strlnode *p0,*q0,*plus0;      unsigned int add,flag=0;      p0 = *p;      q0 = *q;            while(p0->pnext != null)          p0 = p0->pnext;      while(q0->pnext != null)          q0 = q0->pnext;            while(q0 != null && p0 != null)      {         add = p0->data + q0->data + flag;         if(add >= 10)         {             flag = 1;             insertnode(plus,0,add%10);         }else         {             flag = 0;             insertnode(plus,0,add);         }         p0 = p0->plast;         q0 = q0->plast;      }      if(p0 != null || q0 != null)      {         if(p0 == null)             plus0 = q0;         else             plus0 = p0;         while(plus0 != null)         {             add = plus0->data + flag;             if(add >= 10)             {                 flag = 1;                 insertnode(plus,0,add%10);             }else             {                 flag = 0;                 insertnode(plus,0,add);             }             plus0 = plus0->plast;         }      }      if(flag == 1)         insertnode(plus,0,1);      add = getnodenum(plus);      deletenode(plus,add-1); }   如果作为练习链表的训练，这样做可以理解，否则这些操作都太昂贵，这样的大数是不能拿来用的。  哦，你真是拿这个去做题了，那肯定不行的