c中一個利用 副作用 返回值容易出現的誤區

        今天一個學弟問了了一個數據結構的問題.他學習是很好的.而且程序中也能顯示出編程功底,但這個程序他卻沒有得到自己想的的結果,很多人編程的時候都會遇到這樣的問題,我也遇到過不少.下面先看這個小小程序,它是一個關於,用先根法建立並輸出一棵樹的程序:

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
 char data;
 struct node *lchild,*rchild;
}BNode;
void createTree(BNode *r){
 char x;
 scanf("%c",&x);
 if(x=='*')r=NULL;
 else{
  r=(BNode*)malloc(sizeof(BNode));
  r->data=x;
  createTree(r->lchild);
   createTree(r->rchild);
 }
}
void PreorderTraverse(BNode *r){
 if(r != NULL){
  printf("%c",r->data);
  PreorderTraverse(r->lchild);
  PreorderTraverse(r->rchild);
 }
}
void main(void)
{
 int i=0;
 BNode *root=NULL;
 printf("Please enter the member in the mode of PreorderTraverse(*=NULL):/n");
 createTree(root);
 printf("the tree is:/n");
 PreorderTraverse(root);
}

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

       一眼看上去(可能看幾眼都是這樣的),會發現,這個程序挺對的.而且書寫格式,還是其它的,都很標準.可是程序執行不出結果,爲什麼呢...這是個很好的問題.可以給我們自己一個提醒,它讓我們再一次去思考,副作用用作返回值本質是怎麼樣的.以前可能只是知道怎樣去用,可是確從來沒有想過爲什麼會這樣...

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

      函數參數的副作用,實質是,我把我的地址告訴你,你可以對這地址進行操作,(就是說,這地地址我在函數內也可以操作,本身也可以對其操作),然後,我在函數裏面對它裏面的值進行修改,當函數返回的時候,外面的值再去用,這塊內存的話,就是修改過後的值了..而正常的傳值,是傳了一份實參的拷貝,作爲形參,這樣,函數內外就有兩塊一樣的內存了,函數要返回的話,就得用return了.

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

     所以,爲了讓函數內操作函數外的內存(即,用副作用去實現函數返回值),那麼在函數內一定是對形參進行解引用去操作,而要是對形參本身進行操作了,那麼對不起,相當於你放棄了對給你傳進來的那塊內存的操作.這就是上面那題的問題所在.

 

     知道問題了,相應的解決方法也就很明顯了,一種是就不用副作用了,直接用return來返回,把傳值和返回完全分開.第二種就是,還是用副作用,上面我是想對root這個指針本身的值改變(而不是它指向的地址.況且它開始也沒指向任何地方),改變它本身的值,可以把它的地址傳進去(不錯,就是用二指針.)

 

     下面是我修改後的程序:

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

第一種:(用return)

 

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
 char data;
 struct node *lchild,*rchild;
}BNode;
BNode* createTree(BNode *r){
 char x;
 scanf("%c",&x);
 if(x=='*')r=NULL;
 else{
  r=(BNode*)malloc(sizeof(BNode));
  r->data=x;
  r->lchild=createTree(r->lchild);
  r->rchild=createTree(r->rchild);
 }

return r;
}
void PreorderTraverse(BNode *r){
 if(r != NULL){
  printf("%c",r->data);
  PreorderTraverse(r->lchild);
  PreorderTraverse(r->rchild);
 }
}
void main(void)
{
 int i=0;
 BNode *root=NULL;
 printf("Please enter the member in the mode of PreorderTraverse(*=NULL):/n");
 root=createTree(root);
 printf("the tree is:/n");
 PreorderTraverse(root);
}

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

第二種:(二級指針)

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
 char data;
 struct node *lchild,*rchild;
}BNode;
void createTree(BNode **r){
 char x;
 scanf("%c",&x);
 if(x=='*')*r=NULL;
 else{
  *r=(BNode*)malloc(sizeof(BNode));
  (*r)->data=x;
  createTree(&(*r)->lchild);
   createTree(&(*r)->rchild);
 }
}
void PreorderTraverse(BNode *r){
 if(r != NULL){
  printf("%c ",r->data);
  PreorderTraverse(r->lchild);
  PreorderTraverse(r->rchild);
 }
}
void main(void)
{
 int i=0;
 BNode *root=NULL;
 printf("Please enter the member in the mode of PreorderTraverse(*=NULL):/n");
 createTree(&root);
 printf("the tree is:/n");
 PreorderTraverse(root);
}

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

       這個問題已經解決了,下面,說一下關於傳址的一些其它方面的事,主要是程序安全方面的.

 

       想像一下,我把我的地址暴露給你,你可以操作,那我肯定很不爽了.這樣很多問題就來了.

    

       1>我把我的內存給你了,你給我釋放了怎麼辦,我並不知道啊,

       2>如果我們是純種間的調用的話,問題就更多了,我要的釋放了我的空間,同樣,你在不知道,這裏就涉及到了純種間臨界資源訪問的問題了,

這裏就先不多說了,只要大家都能想到這個嚴重問題就可以了.

       3>C++中的這種就涉及到了內存管理問題,我應該在什麼時候回收內存呢,(因爲有多個指針操作同一塊空間,我並不知道什麼時候這塊空間會沒人用了),在析構裏回收顯然是不行的.這裏有個很好的解決方法,就是用句柄類代替指針,(c語言中可以用指針的訪問控制,其它,句柄就相當於對訪問控制的一個封裝),關於句柄,可以看之前寫的文章.

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

      最後,祝大家都學習愉快!!~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章