官术网_书友最值得收藏!

2.3 指針

2.3.1 指針概念的回顧

指針用于存放一個對象在內存中的地址,通過指針能夠間接地操作這個對象。指針的典型用法是建立鏈接的數據結構,如樹(tree)和鏈表(list),并管理在程序運行過程中動態分配的對象,或者用于函數參數,以便傳遞數組或大型的類對象。

對于類型T,T*是到T的指針,即一個類型為T*的變量能夠保存一個類型為T的對象的地址。例如:

int i=10;     //L1
int *p;      //L2
p=&i;       //L3

語句L2定義了一個整型指針變量,它表示p能夠存放一個整型變量的內存地址。L3將變量i的地址存放到指針變量p所代表的內存單元中,如下所示:

有人說,指針代表了兩個變量:一個是指針變量本身,另一個是它所指向的變量。對于語句L2,就可以認為它定義了兩個變量p和*p,p是一個地址變量,只能存放整型變量在內存中的地址;*p是一個整型變量,只能存放一個整數。

指針是一個復雜的概念,它能夠指向(保存)不同類型變量的內存地址。例如:

int *pi;        // pi是指向int的指針
int **pc;        // pc是指向int指針的指針
int *pA[10];      // pA是指向int的指針數組
int (*f)(int,char);  // f是指向具有兩個參數的函數的指針
int *f(int)       // f是一個函數,返回一個指向int的指針

2.3.2 指針與0和void*

沒有任何變量會被分配到地址0,所以0就可以作為一個指針常量,與NULL的含義相同,表明指針當時沒有指向任何變量。例如,對于前面的指針定義,以下賦值都是正確的:

pc=0;
pA=0;
f=0;

指針具有兩個屬性:地址和長度。地址大小固定,與類型無關;長度則與指針類型相關,這個類型指示編譯器怎樣解釋它所指定內存區域的內容,以及該內存區域應該跨越多少個內存單元。例如,在32位微機上,int、float或double類型的指針都是4字節的地址,但int型的指針會指示編譯器存取連續的4字節的內存單元作為一個整數,double型的指針則會指示編譯器連續存取8字節的內存單元作為一個雙精度浮點數……

因此,指針變量本身存儲的僅是一個內存地址,其大小是固定的,而指針類型用于確定如何存取其所指內存區域的數據。鑒于此,C++提供了一種無類型指針void *。兩個void*指針可以相互賦值、比較相等與否。

任何類型的指針都是大小相同的地址,而void*指針僅表示與之相關的值是個地址,因此能夠接收任何數據類型(除函數指針外)的指針。但是,由于void*無法確定其所指內存數據的類型,因此只有顯式地將一個void*轉換成某種數據類型的指針之后,才能訪問其所指內存區域的數據,其他操作都是不允許的。

例2-3】 void*指針的應用。

//Eg2-3.cpp
#include<iostream.h>
void main(){
    int i=4,*pi=&i;
    void* pv;
    double d=9,*pd=&d;
    pv=&i;       //L1:正確
    pv=pi;       //L2:正確
//  cout<<*pv<<endl;   //L3:錯誤
    pv=pd;       //L4:正確
    cout<<*(double*)pv;   //L5:正確,輸出9
}

因為pv是void*指針,無法確定*pv所指內存區域的大小和類型,無法訪問,必須像L5語句那樣經過強制類型轉換之后才能訪問。

void*最重要的用途是作為函數的參數,以便向函數傳遞一個類型可變的對象。另一種用途就是從函數返回一個無類型的對象,在使用時再將它顯式轉換成適當的類型。

2.3.3 new和delete

指針常與堆(heap)空間的分配有關。所謂堆,就是一塊內存區域,它允許程序在運行時以指針的方式從其中申請一定數量的存儲單元(其他存儲空間的分配是在編譯時完成的),用于程序數據的處理。堆內存也稱動態內存。

堆內存的管理由程序員完成。在C語言中,如果需要使用堆內存,程序員可以用函數malloc()從堆中分配指定大小的存儲區域,用完之后必須用函數free()將之歸還系統。如果用完之后沒有用函數free()將它釋放,就會造成內存泄漏(也就是自己不用了,其他程序也無法使用)。因此,函數malloc()和free()在C程序中總是成對出現的。例如:

#include<stdlib.h>       //malloc()和free()定義于此頭文件中
main(){
    int *p;
    //從堆中分配1個int對象需要的內存,并將之轉換為int類型
    p=(int*)malloc(sizeof(int));
    *p=23;
    free(p);          //釋放堆內存
}

malloc( )的使用比較麻煩,除了要計算需求的內存大小之外,還必須對獲得的內存區域進行類型轉換才能使用。為此,C++提供了new和delete兩個運算符進行堆內存的分配與釋放,它們分別與malloc( )和free( )相對應,但使用起來更簡單。

(1)new的用法

new的功能類似于malloc,用于從堆內存中分配指定大小的內存區域,并返回獲得的內存區域的首地址。

用法1:p=new type;
用法2:p=new type(x);
用法3:p=new type[n];

其中,p是指針變量,type是數據類型。用法1只分配堆內存,用法2將分配到的堆內存初始化為x,用法3分配具有n個元素的數組。new能夠根據type自動計算分配的內存大小,不需要用sizeof()計算。如果分配成功,會將得到的堆內存的首地址存放在指針變量p中;如果分配不成功,則返回空指針(0),在程序中可以此作為判斷內存分配成功與否的依據。

(2)delete的用法

delete的功能類似于free,用于釋放new分配的堆內存,以便它被其他程序使用。

用法1:delete p;
用法2:delete []p;

其中,p是用new分配的堆空間指針變量。用法1用于釋放動態分配的單個指針變量,用法2用于釋放動態分配的數組存儲區域。

例2-4】 用new和delete分配與釋放堆內存。

//Eg2-4.cpp
#include <iostream.h>
int main(){
    int *p1,*p2,*p3;
    p1=new int;    //分配一個能夠存放int類型數據的內存區域
    p2=new int(10);  //分配一個int類型大小的內存區域,并將10存入其中
    p3=new int[10];  //分配能夠存放10個整數的數組區域
    if(!p3){               //程序中常會見到這樣的判定
        cout<<"allocation failure"<<endl; //分配不成功,就顯示錯誤信息
        return 1;            //終止程序,并返回錯誤代碼
    }
    *p1=5;
    *p3=1;
    p3[1]=2;        //訪問指向數組的數組元素
    p3[2]=3;
    cout<<"p1  address: "<<p1<<" value: "<<*p1<<endl;
    cout<<"p2  address: "<<p2<<" value: "<<*p2<<endl;
    cout<<"p3[0] address: "<<p3<<" value: "<<*p3<<endl;
    cout<<"p3[1] address: "<<&p3[1]<<" value: "<<p3[1]<<endl;
    delete p1;       //釋放p1指向的內存
    delete p2;
    delete p3;     //錯誤,只釋放了p3指向數組的第1個元素
    delete []p3;     //釋放p3指向的數組
    return 0;
}

delete p3和delete []p3是有區別的,前者只釋放了第一個數組元素,即p[0],而沒有釋放其他數組元素p[1]~p[9],會造成內存泄漏;后者則將p3指向的數組區域全部歸還系統。

(3)new、delete和malloc、free的區別

在C++程序中,仍然可以使用malloc、free進行動態存儲空間的管理,但它們沒有用new、delete方便。主要體現在以下幾方面。

① new能夠自動計算要分配的內存大小,不必用sizeof()計算所要分配的內存字節數,減少了出錯的可能性。

② new不需要進行類型轉換,能夠自動返回正確的指針類型。

③ new可以對分配的內存進行初始化。

④ new和delete可以被重載,程序員可以借此擴展new和delete的功能,建立自定義的存儲分配系統。

主站蜘蛛池模板: 林西县| 凤山县| 金昌市| 龙里县| 成安县| 汝南县| 什邡市| 昂仁县| 曲沃县| 河池市| 五台县| 镇雄县| 印江| 固始县| 图木舒克市| 竹溪县| 上高县| 白水县| 南漳县| 海南省| 祁门县| 肥东县| 榆树市| 德化县| 阳泉市| 昌黎县| 海宁市| 凌云县| 马尔康县| 年辖:市辖区| 兰州市| 徐闻县| 新宁县| 丽水市| 南木林县| 酉阳| 太康县| 萍乡市| 兴安盟| 满城县| 彭泽县|