亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        指針與函數(shù)

        2008-12-31 00:00:00王立柱
        計(jì)算機(jī)教育 2008年7期

        存儲(chǔ)和處理是程序設(shè)計(jì)的基本矛盾。存儲(chǔ)中也有處理,是基本處理,例如,機(jī)器指令中的操作碼,C語言內(nèi)置類型中的運(yùn)算符。隨著處理越來越復(fù)雜,程序設(shè)計(jì)的基本矛盾不斷向前發(fā)展,從而推動(dòng)了程序語言的發(fā)展。指針(在機(jī)器語言中是地址)是存儲(chǔ)和處理的“媒介”、“中介”,是語言的要素,它隨著處理越來越復(fù)雜也在同時(shí)向前發(fā)展。

        1函數(shù)參數(shù)與指針

        C語言程序是由函數(shù)構(gòu)成的,函數(shù)表示處理,實(shí)參表示存儲(chǔ),函數(shù)的指針參量表示存儲(chǔ)和處理的中介,實(shí)參初始化形參,函數(shù)通過指針處理存儲(chǔ)中的數(shù)據(jù)。以表1為例。

        在下面的函數(shù)原型中,形參pa的聲明是等價(jià)的,都表示指針,都是存儲(chǔ)與處理的中介:

        int Sum(int *pa,int n);

        int Sum(int pa[6],int n);

        int Sum(int pa[],int n);

        2模塊化設(shè)計(jì)與指針

        一組存儲(chǔ)中的數(shù)據(jù)通過傳址在函數(shù)之間傳遞。如果這組數(shù)據(jù)是“只讀”的,那么如何保證它不被改寫?在模塊化程序設(shè)計(jì)中,程序按模塊編譯,如果在模塊單獨(dú)編譯階段就對(duì)“只讀”數(shù)據(jù)的安全性進(jìn)行控制,即保證“只讀”數(shù)據(jù)把地址傳給的是“只讀”函數(shù),就會(huì)減少連接調(diào)試階段的工作負(fù)擔(dān)。const限定修飾符便是這種控制的工具。

        const限定符既可以限定存儲(chǔ)中的“只讀”數(shù)據(jù),也可以限定“只讀”函數(shù)。被const修飾的數(shù)據(jù)稱為const常量,它必須初始化;被const修飾的函數(shù)具有被const修飾的指針參量,這個(gè)指針稱為指向const常量的指針,表示函數(shù)對(duì)該指針指向的數(shù)據(jù)是“只讀”的。const常量的聲明格式為:

        const 類型標(biāo)識(shí)符 變量標(biāo)識(shí)符=初始化數(shù)據(jù);

        類型標(biāo)識(shí)符 const 變量標(biāo)識(shí)符=初始化數(shù)據(jù);

        指向const常量的指針,其聲明格式為:

        const 類型標(biāo)識(shí)符 *指針變量標(biāo)識(shí)符;

        類型標(biāo)識(shí)符 const *指針變量標(biāo)識(shí)符;

        應(yīng)用舉例:

        void Display(const int *pa,int n);//終端顯示。“只讀”函數(shù)。

        void Selection(int *pa,int n);

        //選擇排序。非“只讀”函數(shù)。

        const int a[5]= {1,3,2,5,4};

        //const常量數(shù)組。

        int b[5]= {1,3,2,5,4};

        //非const常量數(shù)組。

        Selection(a,5);//非法!

        Display(a,5);//合法。

        Selection(b,5);//合法。

        Display(b,5);//合法。

        指針是復(fù)合類型,它有兩個(gè)值,一個(gè)是指針自身的數(shù)據(jù)(無符號(hào)整型值),表示地址,另一個(gè)是它指向的數(shù)據(jù)(指針基類型值),是指針間接引用的對(duì)象。const修飾的部分不同,意義不同。

        如果const修飾的是指針指向的數(shù)據(jù),那么它是在修飾在修飾函數(shù),表示以該指針為參量的函數(shù)對(duì)該指針指向的數(shù)據(jù)是“只讀”的,該指針就是指向const常量的指針。對(duì)這樣的指針,有下面幾點(diǎn)需要認(rèn)識(shí):

        ① 數(shù)據(jù)無論是不是const常量型,都可以傳址給指向const常量的指針。例如上面的調(diào)用語句Display(b,5),其中數(shù)組b并不是const常量型的。用實(shí)參和形參的關(guān)系來表示便是

        const int *pa=b;

        但是const常量型數(shù)據(jù)只能傳址給代表函數(shù)“只讀”性質(zhì)的指向const常量的指針??梢园褞в兄赶騝onst常量指針參量的函數(shù)比作一個(gè)認(rèn)真辦事的人,什么樣的事情交給他,他都認(rèn)真處理,而一件需要認(rèn)真處理的事情一定要交給他。

        ② 因?yàn)橹赶騝onst常量的指針表示的是函數(shù)的“只讀”性質(zhì),而不是指針本身的數(shù)據(jù)的只讀性質(zhì),所以與const常量不同,這種指針不必初始化。例如:

        const int *pa=b;

        可以分解為

        const int *pa;

        pa=b;

        而且對(duì)它本身的數(shù)據(jù)可以改變,例如:

        const int *pa;

        pa=b;//指向數(shù)組b

        pa=a;//又指向數(shù)組a

        ③ 傳遞性。指向const常量的指針表示的是函數(shù)的“只讀”性質(zhì),任何數(shù)據(jù)傳址給這樣的指針,不僅具有這種指針參量的函數(shù)對(duì)該數(shù)據(jù)是“只讀”的,而且該函數(shù)調(diào)用的其它函數(shù)對(duì)該數(shù)據(jù)也是“只讀”的,這就是說,指向const常量的指針只能傳值給同類指針。仿佛一個(gè)認(rèn)真辦事的人,什么事情交給他,他都認(rèn)真處理,不僅如此,他所尋求的合作伙伴,也一定是認(rèn)真辦事的人。例如:

        void Display(const int*pa,int n);//輸出函數(shù)。

        int Sum(const int* ps,int n)//求和函數(shù)。

        {

        Display(ps,n);// const int* pa=ps;

        ……

        }

        ④ 引入const修飾符之后,任何函數(shù),如果對(duì)某一指針參量指向的數(shù)據(jù)是“只讀”的,都必須把該指針參量限定為指向const常量的指針,表明該函數(shù)的“只讀”性質(zhì),以保證const常量型數(shù)據(jù)通過傳址調(diào)用該函數(shù),被編譯器檢錯(cuò)。

        如果const修飾的是指針本身,那么它是在修飾數(shù)據(jù),表示指針本身的值const常量型的,這樣的指針稱為const常量指針。與const常量型一樣,const常量指針必須初始化,而且其值不能改變。聲明格式為:

        類型標(biāo)識(shí)符 *const指針標(biāo)識(shí)符=初始化數(shù)據(jù);

        例如:

        const int a[5]= {1,3,2,5,4};//const常量數(shù)組。

        int b[5]= {1,3,2,5,4};//非const常量數(shù)組。

        int c[5]= {1,3,2,5,4};//非const常量數(shù)組。

        int *const pc=b;//const常量指針必須初始化。

        pc=c;//非法!const常量指針的值不能改變。

        因?yàn)閏onst常量指針不是限定函數(shù),對(duì)它指向的數(shù)據(jù)可以修改,所以不能把const常量型數(shù)據(jù)的地址賦給const常量指針。例如:

        pc[0]=10;//合法。

        int *const ps=a;//非法。

        3運(yùn)算符函數(shù)與指針

        3.1運(yùn)算符函數(shù)

        運(yùn)算符處理的對(duì)象如果是語言內(nèi)置基本類型(整型、浮點(diǎn)型、字符型等),它的意義是內(nèi)定的。如果是用戶定義的結(jié)構(gòu),意義就是待定的。以結(jié)構(gòu)數(shù)組的查找Find為例:

        struct Student//用戶結(jié)構(gòu)

        {

        long ID;double g;//ID表示學(xué)號(hào),g表示成績(jī)。

        };

        typedef Student Type;//形式數(shù)據(jù)類型Type。

        int Find(const Type *pa,int n,Type item)//查找。

        {

        for(int i=0;i

        if(pa[i]==item)//待定。

        return(i);

        return(-1);

        }

        陰影部分中的關(guān)系運(yùn)算對(duì)象是結(jié)構(gòu),系統(tǒng)無法確定是比較學(xué)號(hào)還是比較成績(jī)。我們可以進(jìn)入函數(shù)體直接改造:

        if(pa[i].g==item.g)

        不過這是權(quán)宜之計(jì)。結(jié)構(gòu)各式各樣,數(shù)組的處理程序數(shù)不勝數(shù),都一一改造嗎?這顯然不符合代碼的復(fù)用性要求。解決這個(gè)問題的方法是運(yùn)算符重載。

        運(yùn)算符重載的思路是,首先把以內(nèi)置類型為處理對(duì)象的運(yùn)算符從觀念上看作函數(shù),然后通過對(duì)該函數(shù)重載,擴(kuò)大運(yùn)算符的操作對(duì)象。這樣的函數(shù)稱為運(yùn)算符函數(shù),運(yùn)算符函數(shù)名為operator @,@代表某一種運(yùn)算符。運(yùn)算符重載就是運(yùn)算符函數(shù)重載。

        以比較運(yùn)算符“==”為例,首先把該運(yùn)算符從觀念上看作一個(gè)函數(shù):

        int operator==(int,int);

        于是兩個(gè)整數(shù)的比較運(yùn)算表達(dá)式

        x==y

        被看作運(yùn)算符函數(shù)的調(diào)用

        Operator== (x,y

        然后反過來,重載運(yùn)算符函數(shù)operator==:

        bool operator== (Student a,Student b)//重載運(yùn)算符函數(shù)的定義

        {

        return(a.g==b.g); //比較成績(jī)

        }

        重載之后,運(yùn)算符“==”的處理對(duì)象就增加了結(jié)構(gòu)Student。具體的執(zhí)行過程是,編譯器如果發(fā)現(xiàn)內(nèi)部無法解釋的運(yùn)算符處理,就會(huì)去尋找重載的運(yùn)算符函數(shù),找到之后,調(diào)用這個(gè)函數(shù)。例如,函數(shù)Find中的表達(dá)式

        pa[i]==item

        被編譯器替換成

        operator==(pa[i],item)

        運(yùn)算符重載是函數(shù)的一種調(diào)用形式。對(duì)用戶自定義類型重載的運(yùn)算符運(yùn)算,可以等價(jià)地表示為運(yùn)算符函數(shù)的調(diào)用,但是內(nèi)部基本類型的運(yùn)算符運(yùn)算是內(nèi)定的,不能實(shí)際的替換成運(yùn)算符函數(shù)的調(diào)用形式,例如,不能把表達(dá)式5==6替換為operator==(5,6)。

        3.2引用

        運(yùn)算符重載函數(shù)的參量不能全部是語言內(nèi)置基本類型,至少要有一個(gè)是用戶定義類型,以免和內(nèi)置基本類型的運(yùn)算符沖突。舉例說明,如果我們想把雙浮點(diǎn)型擴(kuò)展為求余運(yùn)算%的對(duì)象,那么下面的運(yùn)算符重載是不行的:

        double operator%(double a,double b)//非法!參量缺少用戶類型

        {

        return((long)a%(long)b);

        }

        因?yàn)檫@樣的運(yùn)算符函數(shù)與浮點(diǎn)型基本運(yùn)算沖突,使編譯器失去了檢錯(cuò)能力。

        一個(gè)可行的方法是,首先創(chuàng)建一個(gè)用戶結(jié)構(gòu)類型表示雙浮點(diǎn)型:

        struct DOUBLE//創(chuàng)建一個(gè)用戶結(jié)構(gòu)類型表示雙浮點(diǎn)型

        {

        double f;

        };

        然后運(yùn)算符重載如下:

        double operator%(DOUBLE A,double b)//參量A是用戶類型

        {

        return((long)A.f%(long)b);

        }

        應(yīng)用舉例:

        DOUBLE x={13.1};

        double y=4.5;

        cout<<(x%y);//結(jié)果是1

        可是,新的問題出現(xiàn)了。運(yùn)算符函數(shù)是值調(diào)用,值調(diào)用的實(shí)質(zhì)是參數(shù)復(fù)制,即實(shí)參復(fù)制給形參,而運(yùn)算符函數(shù)的參數(shù)主要是結(jié)構(gòu),結(jié)構(gòu)可以很大,參數(shù)復(fù)制既占空間,又費(fèi)時(shí)間,加之,運(yùn)算符的使用頻率高,綜合起來考慮,為運(yùn)算符函數(shù)的值調(diào)用而需要付出的時(shí)空代價(jià)是令人難以承受的。解決這個(gè)問題的方法自然想到地址調(diào)用,因?yàn)椴徽搮?shù)多大,其地址需要的單元只是2個(gè)字節(jié)或4個(gè)字節(jié)(因系統(tǒng)而定),效率有了保證。可是地址調(diào)用的參量都是指針,而指針是語言內(nèi)置類型,在上一節(jié)最后我們已經(jīng)指出,運(yùn)算符函數(shù)的參量至少要有一個(gè)是用戶類型,因此下面的運(yùn)算符重載是非法的。

        bool operator== (const Student*a,const Student*b)//非法!

        {

        return(a->g==b->g);//比較成績(jī)

        }

        我們可以做如下改進(jìn),使某一個(gè)參量不是內(nèi)置類型:

        bool operator== (Student a,const Student*b)

        {

        return(a.g==b->g);//比較成績(jī)

        }

        于是有:

        if(pa[i]==item)// if(operator==(pa[i],iem))

        return(i);

        但是,表達(dá)式“pa[i]==item”把運(yùn)算符的簡(jiǎn)潔形式“pa[i]==item”破壞了,而且第1個(gè)參量仍然是值傳遞。

        運(yùn)算符重載給我們提出了一個(gè)難題:運(yùn)算符函數(shù)既要具備地址調(diào)用的效率,又要保留值調(diào)用的簡(jiǎn)潔自然的形式。解決這個(gè)難題的方法就是引用型。引用的聲明格式為:

        類型標(biāo)識(shí)符 引用=被引用的變量;

        舉例說明:

        int x=5;

        int y=x;//定義一個(gè)引用,引用必須初始化

        稱y是x的引用,或x是被y引用的變量。

        引用的實(shí)質(zhì)是指針。在內(nèi)部,引用是指針,而且它必須初始化,取得被引用變量的地址,初始化值不能改變。語句int y=x在內(nèi)部相當(dāng)于int* y=x。

        在外部,對(duì)用戶來說,聲明之后的引用名稱不再表示指針,而是表示指針指向的變量,相當(dāng)于前面有一個(gè)隱藏的運(yùn)算符“*”。例如:

        y=6;//內(nèi)部相當(dāng)于*y=6;

        因此,人們從形式上把引用y看作是被引用變量x的別名或同義詞,也就是說y就是x。如圖1所示。

        在內(nèi)部,引用相當(dāng)于const常量指針。在外部,引用與const常量指針不同,對(duì)它本身既不能取址也不能取值,因?yàn)樗潜灰玫淖兞康膭e名,例如,y表示的是x的地址,而不是y指針的地址;y的值是x的值,而不是y指針的值即x的地址。

        可以用下面一個(gè)簡(jiǎn)單方法來驗(yàn)證“引用的實(shí)質(zhì)是指針”。我們知道,一個(gè)函數(shù)的自動(dòng)局部變量地址不能是函數(shù)返回值,因?yàn)楹瘮?shù)調(diào)用之后,其自動(dòng)局部變量的生命周期結(jié)束,空間被撤消,返回它的地址是沒有意義的。例如:

        int* Func2(void)

        {

        int x=5;

        return(x);//int*temp=x;錯(cuò)誤!不能返回自動(dòng)局部變量地址

        }

        編譯器錯(cuò)誤提示為:returning address of local variable or temporary(返回值是一個(gè)局部變量或臨時(shí)變量的地址)。當(dāng)我們返回一個(gè)自變量的引用時(shí),編譯器的錯(cuò)誤提示是相同的:

        int Func2(void)

        {

        int x=5;

        return(x); //int temp=x; 錯(cuò)誤!不能返回自動(dòng)局部變量地址

        }

        把運(yùn)算符函數(shù)的參量設(shè)為引用型,問題就得到了解決:

        bool operator== (const Student a,const Student b)

        {

        return(a.g==b.g); //比較成績(jī)

        }

        typedef Student Type;

        int Find(const Type *pa,int n,Type item)//查找。

        {

        for(int i=0;i

        if(pa[i]==item)//if(operator==(pa[i], item))

        return(i);

        return(-1);

        }

        引用型參量a和b的實(shí)質(zhì)是指針,相當(dāng)于const常量指針,而運(yùn)算符函數(shù)operator==是“只讀”的,它的指針參量應(yīng)該是指向const常量的指針,所以a和b的實(shí)質(zhì)是指向const常量的const常量指針,而它們的名稱是const常量型引用。

        3.3基本類型運(yùn)算符中的引用

        地址是處理和數(shù)據(jù)之間的“媒介”、“中介”,它是程序語言的要素,一開始就包含在機(jī)器指令這個(gè)程序語言的細(xì)胞中,例如,機(jī)器指令的操作數(shù)一般是數(shù)據(jù)的地址。進(jìn)入到C語言,地址發(fā)展為指針,它就應(yīng)該包含在基本類型的運(yùn)算符表達(dá)式中。以下面的賦值表達(dá)式為例:

        (x=y)=z

        執(zhí)行過程是,y的值給x,z的值給x,結(jié)果是x和z的值相等。

        從概念上用復(fù)合運(yùn)算符函數(shù)表示為:

        operator=(operator=(x,y),z)

        這不僅要求運(yùn)算符函數(shù)operator=的第1個(gè)參量是引用,而且返回值也是第1個(gè)參量的引用。為了理解,我們以用戶定義的結(jié)構(gòu)Student為例,重載賦值運(yùn)算符:

        Student operator= (Student a,const Student b)

        {

        a.ID=b.ID;

        a.g=b.g;

        return(a);//Student _temp=a;

        }

        由此說明,引用是指針發(fā)展的一種較高級(jí)的形式。運(yùn)算符重載是引用產(chǎn)生的必要性,而基本數(shù)據(jù)類型運(yùn)算符包含著它產(chǎn)生的可能性。

        有人可能要問,在基本類型的賦值表達(dá)式中,操作數(shù)可以是字面值常量,例如:

        (x=3)=4

        那么既然形參是引用,而且引用的實(shí)質(zhì)是指針,那么實(shí)參就必須傳址,可是字面值常量3和4是不能尋址的。問題是能夠這樣解決的:如果實(shí)參是字面值常量,系統(tǒng)就開辟一個(gè)臨時(shí)的const常量型空間來存儲(chǔ)實(shí)參,然后將const常量型空間的地址傳遞為形參[1]。

        4通用算法與指針

        C++標(biāo)準(zhǔn)模板庫STL的主要組件是容器類、通用算法和迭代器。容器類和通用算法在更高級(jí)上分別代表著存儲(chǔ)和處理,迭代器是它們的中介,迭代器是指針的更高級(jí)形式,是一種smart pointers。

        “STL的中心思想在于:將數(shù)據(jù)容器(containers)和算法(algorithms)分開,彼此對(duì)立設(shè)計(jì),最后再以一帖膠著劑將它們撮合在一起。容器和算法的泛型化,從技術(shù)角度來看并不困難,C++的class templates和function templates可分別達(dá)成目標(biāo)。但是如何設(shè)計(jì)出兩者之間的良好膠著劑,才是大難題”。[2]

        有關(guān)具體內(nèi)容將在后期引入C++后進(jìn)一步討論。

        5小結(jié)

        存儲(chǔ)和處理是程序設(shè)計(jì)的基本矛盾,處理的不斷復(fù)雜,推動(dòng)了這個(gè)矛盾的不斷發(fā)展,進(jìn)而也推動(dòng)了程序語言的不斷發(fā)展。地址、指針、指向const常量的指針、引用和迭代器是處理和存儲(chǔ)的“媒介”在程序語言發(fā)展中的一系列進(jìn)化。

        參考文獻(xiàn)

        [1] 王立柱.C/C++與數(shù)據(jù)結(jié)構(gòu)(第3版上)[M]. 北京: 清華大學(xué)出版社,2008. 215.

        [2] 侯捷.STL源碼剖析[M]. 武昌: 華中科技大學(xué)出版社, 2002. 79.

        人妻少妇看a偷人无码精品| 国产白浆一区二区在线| 国产av无码专区亚洲精品| 久久久久久好爽爽久久| 男人的天堂在线无码视频| 亚洲高清在线视频网站| 精品久久久少妇一区二区| 69一区二三区好的精华| 91av在线播放| 日韩精品一区二区三区四区五区六| 中文字幕乱码亚洲一区二区三区| 高潮抽搐潮喷毛片在线播放| 国内精品无码一区二区三区| 亚洲欧洲日产国码无码| 日韩av在线手机免费观看| 国产麻豆精品精东影业av网站| 亚洲一区精品无码色成人| 91久久国产综合精品| 亚洲av无吗国产精品| 日韩久久无码免费毛片软件| 成人做爰视频www| 国产成人一区二区三区高清| 国产午夜福利小视频在线观看| 免费看美女被靠到爽的视频| 日韩a无v码在线播放| 亚洲Va中文字幕无码毛片下载| 黑丝美腿国产在线观看| 亚洲欧美日韩另类精品一区 | 91精品一区国产高清在线gif| 午夜av内射一区二区三区红桃视| 一区二区三区中文字幕在线播放| 国产午夜毛片v一区二区三区| 亚洲美腿丝袜综合一区| 免费的黄网站精品久久| 亚洲精品国产第一区二区| 无码国产精品一区二区高潮| 国产视频网站一区二区三区| 99精品人妻少妇一区二区三区| 亚洲日韩中文字幕无码一区| 日本色噜噜| 男女啦啦啦视频在线观看|