1. 程式人生 > >我的經驗:指標和指標的引用

我的經驗:指標和指標的引用

我的經驗:指標和指標的引用


我一下討論的都是在不用return把值進行返回的情況, 也就是說用指標的情況。

很簡單就是指標和引用,引用在新版本的c裡面也有包含,可以使用。

系統xp+ide用的 vc6.0:

要把一個值通過呼叫函式改變,可以用指標和引用,這個值可以是int int *,char char*,或者說是結構體,結構體也有不同的情況,比如說,

結構裡面有結構體指標和沒有的情況,這裡我將就我的經歷寫一下。

先看看人家的比較好的說法:

   void func1( MYCLASS *&pBuildingElement );

   仔細看一下這種宣告方式,確實有點讓人迷惑。在某種意義上,"*"和"&"是意思相對的兩個東西,把它們放在一起有什麼意義呢?。為了

理解指標的這種做法,我們先複習一下C/C++程式設計中無所不在的指標概念。我們都知道MYCLASS*的意思:指向某個物件的指標,此物件的型別為

MYCLASS。 void func1(MYCLASS *pMyClass);  

  例如: MYCLASS* p = new MYCLASS;

或者你也可以用malloc函式在c下面申請一片記憶體地址。

   func1(p);

   上面這段程式碼的這種處理方法想必誰都用過,建立一個MYCLASS物件,然後將它傳入func1函式。現在假設此函式要修改pMyClass: void

func1(MYCLASS *pMyClass)

   {
   DoSomething(pMyClass);
   pMyClass = // 其它物件的指標
   } 

   第二條語句在函式過程中只修改了pMyClass的值。並沒有修改呼叫者的變數p的值。如果p指向某個位於地址0x008a00的物件,當func1返

回時,它仍然指向這個特定的物件。(除非func1有bug將堆弄亂了,完全有這種可能。)

  現在假設你想要在func1中修改p的值。這是你的權利。呼叫者傳入一個指標,然後函式給這個指標賦值。以往一般都是傳雙指標,即指標

的指標,例如,CMyClass**。 MYCLASS* p = NULL;

   func1(&p);
   void func1(MYCLASS** pMyClass);
   {
   *pMyClass = new MYCLASS;
   ……
   }  

  呼叫func1之後,p指向新的物件。

   如果你理解指標的指標,那麼你肯定就理解指標引用,因為它們完全是一回事。如果你象下面這樣宣告函式:

   void func1(MYCLASS *&pMyClass);
   {
   pMyClass = new MYCLASS;
   ……
   }  

   其實,它和前面所講得指標的指標例子是一碼事,只是語法有所不同。傳遞的時候不用傳p的地址&p,而是直接傳p本身:  

   MYCLASS* p = NULL;
   func1(p);  

   在呼叫之後,p指向一個新的物件。一般來講,引用的原理或多或少就象一個指標,從語法上看它就是一個普通變數。所以只要你碰到*&

,就應該想到**。也就是說這個函式修改或可能修改呼叫者的指標,而呼叫者象普通變數一樣傳遞這個指標,不使用地址操作符&。


下面是我的程式設計經歷,也可以算是經驗吧:

接著我們對int 和 結構體的情況討論,一般char的都是處理字串很簡單的。

要改變值可以如下,指標和引用:

一般的對int改變值其實可以就可以只用一個*,用兩個指標和引用就是為了加強理解,我門看看:

下面的是用指標的測試函式

1:
void function(int *ref)  //單指標的改變值的函式
{
 
 //ref=(int *)malloc(sizeof(int));這句不能用,如果用了傳入的地址無效,因為這裡又重新分配了一個地址,那麼原來傳入的地址

沒有被操作。
 *ref=100;
 
}
int main()   //測試函式,main();
{
 int *x;
 x=(int *)malloc(4);//先分配地址,要不後面給沒有初始化的指標賦值會出現segments fault。
 *x=200;
 function(x);
 printf("*x=%d/n",*x);
}

當然了main函式也可以這麼設計:

int main()   //測試函式,main();
{
 int x;
 function(&x);
 printf("x=%d/n",x);
}

2:
下面給出雙指標的使用方法:

void function(int **p)  //雙指標的函式
{
 *p=(int *)malloc(sizeof(int));//用了或者不用都可以了,用了也可以改變傳入的地址上的值,這個我就不曉得為什麼了。
          //但是不用當然是可以的,因為呼叫之前定義的指標要分配地址的,傳入的地址沒有segments

fault就可以了。我不用的。
          //記住一點對與雙指標的地址操作都是對*p來的,不是對p,而單指標就是上面的函式地址操作就是

對p來的。呵呵。上面的兩個function可以說是很通用了。
 **p=100;
 
}  
int main()   //測試函式,main();
{
 int *x;
 x=(int *)malloc(4);//先分配地址,要不後面給沒有初始化的指標賦值會出現segments fault。
 *x=200;
 function(&x);
 printf("x=%d/n",*x);
}

看完了指標就看引用了,&這個符號是一個二義符號,不可以認為是取地址的。

看函式:


3:
void function(int &refval)
{
 refval=100;
}

int main()
{
 int x=200;
 function(x);
 //當然,如下呼叫也可以。但這樣做就失去引入"引用"的原本意義了,不過引用就是引用,作用物件refval的值和地址都和x的值和地址一樣

了,可以認為是捆綁式的了。呵呵
 printf("x=%d/n",x);
 return 0;
}

4:
再看指標的引用:其實指標的引用可以理解成雙指標,只是其地址式p而不是*p,呵呵。

看這個程式:程式比較多呵呵

#include <stdio.h>
#include <malloc.h>

void function(int *&ref)
{
 
 //ref=(int *)malloc(sizeof(int));//這裡同雙指標,分配或者不分配都是可以的。
 *ref=100;
 
}


int main()
{
 int *x;  //要這種定義才可以使用。  
 x=(int *)malloc(4);
 *x=200;
 function(x);
 printf("x=%d/n",*x);

 return 0;
}

//這裡也有點研究的必要,如果像下面這個給出main函式:

int main()
{

 int x=300;
 function(&x);
 printf("x=%d/n",x);
}
編譯通過不了的,很簡單,指標的引用其實本質應該式雙指標,上面的main中的值是給單指標的賦值,顯然也是不可以的。錯誤提示如下:

D:/hehe/hehe/yinyong/yin.cpp(22) : error C2664: 'function' : cannot convert parameter 1 from 'int *' to 'int *& '

對於結構體,有兩種情況,一種就是結構體裡面沒有結構體指標的,要傳值或者說要通過呼叫函式改變他的值,就像上面的int型的使用方法,

這裡就只說說其中的初始化,改變值是一樣的:

先給定結構體:

typedef struct stack {
        int data;
        char *p;
        }st,*Ls;

開始已經說了,不用return方法,就用傳值的方法:

1:

如果在main函式裡面定義成:st list;那麼初始化就是:這裡應該知道,變數的引用就是變數的單指標一樣。
void set(st &list)
{
 list.data=100;
 list.p="asdf";
}

結構體定義:st list;
函式呼叫是:
set(list);
2:

如果在main函式裡面定義成:st *list;那麼初始化就是:
void set(st *list)
{
 list->data=100;
 list->p="asdf";
}

結構體定義:st list;
函式呼叫是:
set(&list);

或者:
結構體定義:st *list;
函式呼叫是:
set(list);
這種情況我覺得不要使用指標的引用。

另外一種就是結構體裡面還有結構體指標,如下定義結構體:

typedef struct stack {
        int data;
        struct stack *next;
        }st,*ls;

初始化的方法有幾種,跟宣告變數有關:

1:雙指標法:

void init(st **head){
     (*head)=(st *)malloc(sizeof(st)); //也可以不分配地址,因為在每次main傳值時,都必須先分配地址空間
     (*head)->next=null;
     }
宣告結構體變數的時候,一般這麼定義,
st *head;
呼叫函式的時候這麼用:
init(st &head);

2:單指標法
這個方法要使用到返回傳值,我們不研究,當然了寫一下:
st *InitializeList(st  *plist)
{
       // plist=(st *)malloc(sizeof (st)); //也可以不用。
     plist = NULL;
     return plist;
}

3另一種雙指標法
其實這種只是在定製結構體型別的時候把st定義成了*ls,然後用ls來定義結構體。

如下:
void InitializeList(ls  *plist)
{
         //plist=malloc(sizeof (List));  //可以不用。
    *plist = NULL; 

}

宣告結構體變數的時候,一般這麼定義,
st *head;
或者
ls head;
呼叫函式的時候都是這麼用:
InitializeList(&head);

這裡我要說的,其實在初始化和使用的時候跟雙指標一個樣子的,使用的是*p而不是p,把他的地址賦值給其他的結構體的時候也要用*plist,

具體的可以看下面的程式;

struct film
{
    char title[TSIZE];
    int rating;
};
typedef struct film Item;
typedef struct node
{
    Item item;
    struct node * next;
} Node;
typedef Node * List;

bool AddItem(Item item, List  * plist)
{
    Node * pnew;
    Node * scan =* plist; //賦地址的時候用* plist

    pnew = (Node *) malloc(sizeof(Node));
    if (pnew == NULL)
        return false;     /* quit function on failure  */
   pnew= CopyToNode(item, pnew);
    pnew->next = NULL;
    if (scan == NULL)          /* empty list, so place */
        *plist = pnew;         /* pnew at head of list */ //賦地址的時候用* plist
    else
    {
        while (scan->next != NULL)
            scan = scan->next;  /* find end of list    */
        scan->next = pnew;      /* add pnew to end     */
    }
  
    return true;
}

4:
最後了,就是指標的引用來初始化內含結構體指標的結構體:
void InitializeList(st  *&plist)
{

     plist=(st *)malloc(sizeof (st)); //也可以不用。
     plist->next = NULL;
    
}
這裡其實跟上面講到的指標的引用是一樣的,跟雙指標差不多,但是使用的時候用plist,plist->next而不是*plist,(*plist)->next。
宣告結構體為:
st  *plist;
呼叫函式:
InitializeList(plist);

到此,我所遇到的一些指標和指標的引用都列舉出來了。有什麼不對,大家可以指點一下哈。 

下面是我的一個指標的引用的具體的程式可以通過:

#include <stdio.h>
#include <malloc.h>
#define null 0
typedef struct linknode
{
 char data;
 struct linknode *next;
}listack;

void initstack(listack *&s)//初始化
{
s=(listack *)malloc(sizeof(listack));
s->next=null;
}

void clear(listack *&s){//釋放堆疊

 listack *p=s->next;
 while(p!=null)
 {
  free(s);
  s=p;
  p=p->next;
 }
}

int length(listack *s){//顯示堆疊長度

 int i=0;
 listack *p;
 p=s->next;
 while(p!=null)
 {
  i++;
  p=p->next;
 }
 return i;
}

int empty(listack *s){//空?

 return (s->next==null);
}

void push(listack *&s,char e){//入堆疊

 listack *p;
 p=(listack *)malloc(sizeof(listack));
 p->data=e;
 p->next=s->next;
 s->next=p;
}

int pop(listack *&s,char e){//出堆疊

 listack *p;
 if(s->next==null)
  return 0;
 p=s->next;
 e=p->data;
 s->next=p->next;
 free(p);
 return 1;
}

int gettop(listack *&s,char e){//顯示第一個資料

 if(s->next==null)
  return 0;
 e=s->next->data;
 return 1;
}

void display(listack *s){//顯示所有資料

 listack *p=s->next;
 while(p!=null){
 
  printf("%c",p->data);
  p=p->next;
 }
 puts("");
}

 

int main()
{


 char e;
 listack *s;
 puts("初始化堆疊/n");
 initstack(s);//指標的引用
 printf("戀棧為%s/n",((empty(s))?"empty":"no empty"));
 push(s,'a');
 push(s,'b');
 push(s,'c');
 push(s,'d');
 push(s,'f');
 push(s,'e');
 printf("戀棧為%s/n",(empty(s)?"empty":"no empty"));
 printf("lenth=%d/n",length(s));
 puts("xianshi/n");
 display(s);
 printf("out stack!/n");
 while(!empty(s)){
  
  pop(s,e);
  printf(" %c",e);
 }
 puts("");
 printf("戀棧為%s/n",((empty(s))?"empty":"no empty"));
 printf("delete the stack/n");
 clear(s);
 return 0;
}

我一下討論的都是在不用return把值進行返回的情況, 也就是說用指標的情況。

很簡單就是指標和引用,引用在新版本的c裡面也有包含,可以使用。

系統xp+ide用的 vc6.0:

要把一個值通過呼叫函式改變,可以用指標和引用,這個值可以是int int *,char char*,或者說是結構體,結構體也有不同的情況,比如說,

結構裡面有結構體指標和沒有的情況,這裡我將就我的經歷寫一下。

先看看人家的比較好的說法:

   void func1( MYCLASS *&pBuildingElement );

   仔細看一下這種宣告方式,確實有點讓人迷惑。在某種意義上,"*"和"&"是意思相對的兩個東西,把它們放在一起有什麼意義呢?。為了

理解指標的這種做法,我們先複習一下C/C++程式設計中無所不在的指標概念。我們都知道MYCLASS*的意思:指向某個物件的指標,此物件的型別為

MYCLASS。 void func1(MYCLASS *pMyClass);  

  例如: MYCLASS* p = new MYCLASS;

或者你也可以用malloc函式在c下面申請一片記憶體地址。

   func1(p);

   上面這段程式碼的這種處理方法想必誰都用過,建立一個MYCLASS物件,然後將它傳入func1函式。現在假設此函式要修改pMyClass: void

func1(MYCLASS *pMyClass)

   {
   DoSomething(pMyClass);
   pMyClass = // 其它物件的指標
   } 

   第二條語句在函式過程中只修改了pMyClass的值。並沒有修改呼叫者的變數p的值。如果p指向某個位於地址0x008a00的物件,當func1返

回時,它仍然指向這個特定的物件。(除非func1有bug將堆弄亂了,完全有這種可能。)

  現在假設你想要在func1中修改p的值。這是你的權利。呼叫者傳入一個指標,然後函式給這個指標賦值。以往一般都是傳雙指標,即指標

的指標,例如,CMyClass**。 MYCLASS* p = NULL;

   func1(&p);
   void func1(MYCLASS** pMyClass);
   {
   *pMyClass = new MYCLASS;
   ……
   }  

  呼叫func1之後,p指向新的物件。

   如果你理解指標的指標,那麼你肯定就理解指標引用,因為它們完全是一回事。如果你象下面這樣宣告函式:

   void func1(MYCLASS *&pMyClass);
   {
   pMyClass = new MYCLASS;
   ……
   }  

   其實,它和前面所講得指標的指標例子是一碼事,只是語法有所不同。傳遞的時候不用傳p的地址&p,而是直接傳p本身:  

   MYCLASS* p = NULL;
   func1(p);  

   在呼叫之後,p指向一個新的物件。一般來講,引用的原理或多或少就象一個指標,從語法上看它就是一個普通變數。所以只要你碰到*&

,就應該想到**。也就是說這個函式修改或可能修改呼叫者的指標,而呼叫者象普通變數一樣傳遞這個指標,不使用地址操作符&。


下面是我的程式設計經歷,也可以算是經驗吧:

接著我們對int 和 結構體的情況討論,一般char的都是處理字串很簡單的。

要改變值可以如下,指標和引用:

一般的對int改變值其實可以就可以只用一個*,用兩個指標和引用就是為了加強理解,我門看看:

下面的是用指標的測試函式

1:
void function(int *ref)  //單指標的改變值的函式
{
 
 //ref=(int *)malloc(sizeof(int));這句不能用,如果用了傳入的地址無效,因為這裡又重新分配了一個地址,那麼原來傳入的地址

沒有被操作。
 *ref=100;
 
}
int main()   //測試函式,main();
{
 int *x;
 x=(int *)malloc(4);//先分配地址,要不後面給沒有初始化的指標賦值會出現segments fault。
 *x=200;
 function(x);
 printf("*x=%d/n",*x);
}

當然了main函式也可以這麼設計:

int main()   //測試函式,main();
{
 int x;
 function(&x);
 printf("x=%d/n",x);
}

2:
下面給出雙指標的使用方法:

void function(int **p)  //雙指標的函式
{
 *p=(int *)malloc(sizeof(int));//用了或者不用都可以了,用了也可以改變傳入的地址上的值,這個我就不曉得為什麼了。
          //但是不用當然是可以的,因為呼叫之前定義的指標要分配地址的,傳入的地址沒有segments

fault就可以了。我不用的。
          //記住一點對與雙指標的地址操作都是對*p來的,不是對p,而單指標就是上面的函式地址操作就是

對p來的。呵呵。上面的兩個function可以說是很通用了。
 **p=100;
 
}  
int main()   //測試函式,main();
{
 int *x;
 x=(int *)malloc(4);//先分配地址,要不後面給沒有初始化的指標賦值會出現segments fault。
 *x=200;
 function(&x);
 printf("x=%d/n",*x);
}

看完了指標就看引用了,&這個符號是一個二義符號,不可以認為是取地址的。

看函式:


3:
void function(int &refval)
{
 refval=100;
}

int main()
{
 int x=200;
 function(x);
 //當然,如下呼叫也可以。但這樣做就失去引入"引用"的原本意義了,不過引用就是引用,作用物件refval的值和地址都和x的值和地址一樣

了,可以認為是捆綁式的了。呵呵
 printf("x=%d/n",x);
 return 0;
}

4:
再看指標的引用:其實指標的引用可以理解成雙指標,只是其地址式p而不是*p,呵呵。

看這個程式:程式比較多呵呵

#include <stdio.h>
#include <malloc.h>

void function(int *&ref)
{
 
 //ref=(int *)malloc(sizeof(int));//這裡同雙指標,分配或者不分配都是可以的。
 *ref=100;
 
}


int main()
{
 int *x;  //要這種定義才可以使用。  
 x=(int *)malloc(4);
 *x=200;
 function(x);
 printf("x=%d/n",*x);

 return 0;
}

//這裡也有點研究的必要,如果像下面這個給出main函式:

int main()
{

 int x=300;
 function(&x);
 printf("x=%d/n",x);
}
編譯通過不了的,很簡單,指標的引用其實本質應該式雙指標,上面的main中的值是給單指標的賦值,顯然也是不可以的。錯誤提示如下:

D:/hehe/hehe/yinyong/yin.cpp(22) : error C2664: 'function' : cannot convert parameter 1 from 'int *' to 'int *& '

對於結構體,有兩種情況,一種就是結構體裡面沒有結構體指標的,要傳值或者說要通過呼叫函式改變他的值,就像上面的int型的使用方法,

這裡就只說說其中的初始化,改變值是一樣的:

先給定結構體:

typedef struct stack {
        int data;
        char *p;
        }st,*Ls;

開始已經說了,不用return方法,就用傳值的方法:

1:

如果在main函式裡面定義成:st list;那麼初始化就是:這裡應該知道,變數的引用就是變數的單指標一樣。
void set(st &list)
{
 list.data=100;
 list.p="asdf";
}

結構體定義:st list;
函式呼叫是:
set(list);
2:

如果在main函式裡面定義成:st *list;那麼初始化就是:
void set(st *list)
{
 list->data=100;
 list->p="asdf";
}

結構體定義:st list;
函式呼叫是:
set(&list);

或者:
結構體定義:st *list;
函式呼叫是:
set(list);
這種情況我覺得不要使用指標的引用。

另外一種就是結構體裡面還有結構體指標,如下定義結構體:

typedef struct stack {
        int data;
        struct stack *next;
        }st,*ls;

初始化的方法有幾種,跟宣告變數有關:

1:雙指標法:

void init(st **head){
     (*head)=(st *)malloc(sizeof(st)); //也可以不分配地址,因為在每次main傳值時,都必須先分配地址空間
     (*head)->next=null;
     }
宣告結構體變數的時候,一般這麼定義,
st *head;
呼叫函式的時候這麼用:
init(st &head);

2:單指標法
這個方法要使用到返回傳值,我們不研究,當然了寫一下:
st *InitializeList(st  *plist)
{
       // plist=(st *)malloc(sizeof (st)); //也可以不用。
     plist = NULL;
     return plist;
}

3另一種雙指標法
其實這種只是在定製結構體型別的時候把st定義成了*ls,然後用ls來定義結構體。

如下:
void InitializeList(ls  *plist)
{
         //plist=malloc(sizeof (List));  //可以不用。
    *plist = NULL; 

}

宣告結構體變數的時候,一般這麼定義,
st *head;
或者
ls head;
呼叫函式的時候都是這麼用:
InitializeList(&head);

這裡我要說的,其實在初始化和使用的時候跟雙指標一個樣子的,使用的是*p而不是p,把他的地址賦值給其他的結構體的時候也要用*plist,

具體的可以看下面的程式;

struct film
{
    char title[TSIZE];
    int rating;
};
typedef struct film Item;
typedef struct node
{
    Item item;
    struct node * next;
} Node;
typedef Node * List;

bool AddItem(Item item, List  * plist)
{
    Node * pnew;
    Node * scan =* plist; //賦地址的時候用* plist

    pnew = (Node *) malloc(sizeof(Node));
    if (pnew == NULL)
        return false;     /* quit function on failure  */
   pnew= CopyToNode(item, pnew);
    pnew->next = NULL;
    if (scan == NULL)          /* empty list, so place */
        *plist = pnew;         /* pnew at head of list */ //賦地址的時候用* plist
    else
    {
        while (scan->next != NULL)
            scan = scan->next;  /* find end of list    */
        scan->next = pnew;      /* add pnew to end     */
    }
  
    return true;
}

4:
最後了,就是指標的引用來初始化內含結構體指標的結構體:
void InitializeList(st  *&plist)
{

     plist=(st *)malloc(sizeof (st)); //也可以不用。
     plist->next = NULL;
    
}
這裡其實跟上面講到的指標的引用是一樣的,跟雙指標差不多,但是使用的時候用plist,plist->next而不是*plist,(*plist)->next。
宣告結構體為:
st  *plist;
呼叫函式:
InitializeList(plist);

到此,我所遇到的一些指標和指標的引用都列舉出來了。有什麼不對,大家可以指點一下哈。 

下面是我的一個指標的引用的具體的程式可以通過:

#include <stdio.h>
#include <malloc.h>
#define null 0
typedef struct linknode
{
 char data;
 struct linknode *next;
}listack;

void initstack(listack *&s)//初始化
{
s=(listack *)malloc(sizeof(listack));
s->next=null;
}

void clear(listack *&s){//釋放堆疊

 listack *p=s->next;
 while(p!=null)
 {
  free(s);
  s=p;
  p=p->next;
 }
}

int length(listack *s){//顯示堆疊長度

 int i=0;
 listack *p;
 p=s->next;
 while(p!=null)
 {
  i++;
  p=p->next;
 }
 return i;
}

int empty(listack *s){//空?

 return (s->next==null);
}

void push(listack *&s,char e){//入堆疊

 listack *p;
 p=(listack *)malloc(sizeof(listack));
 p->data=e;
 p->next=s->next;
 s->next=p;
}

int pop(listack *&s,char e){//出堆疊

 listack *p;
 if(s->next==null)
  return 0;
 p=s->next;
 e=p->data;
 s->next=p->next;
 free(p);
 return 1;
}

int gettop(listack *&s,char e){//顯示第一個資料

 if(s->next==null)
  return 0;
 e=s->next->data;
 return 1;
}

void display(listack *s){//顯示所有資料

 listack *p=s->next;
 while(p!=null){
 
  printf("%c",p->data);
  p=p->next;
 }
 puts("");
}

 

int main()
{


 char e;
 listack *s;
 puts("初始化堆疊/n");
 initstack(s);//指標的引用
 printf("戀棧為%s/n",((empty(s))?"empty":"no empty"));
 push(s,'a');
 push(s,'b');
 push(s,'c');
 push(s,'d');
 push(s,'f');
 push(s,'e');
 printf("戀棧為%s/n",(empty(s)?"empty":"no empty"));
 printf("lenth=%d/n",length(s));
 puts("xianshi/n");
 display(s);
 printf("out stack!/n");
 while(!empty(s)){
  
  pop(s,e);
  printf(" %c",e);
 }
 puts("");
 printf("戀棧為%s/n",((empty(s))?"empty":"no empty"));
 printf("delete the stack/n");
 clear(s);
 return 0;
}