999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

指針與函數

2008-12-31 00:00:00王立柱
計算機教育 2008年7期

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

1函數參數與指針

C語言程序是由函數構成的,函數表示處理,實參表示存儲,函數的指針參量表示存儲和處理的中介,實參初始化形參,函數通過指針處理存儲中的數據。以表1為例。

在下面的函數原型中,形參pa的聲明是等價的,都表示指針,都是存儲與處理的中介:

int Sum(int *pa,int n);

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

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

2模塊化設計與指針

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

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

const 類型標識符 變量標識符=初始化數據;

類型標識符 const 變量標識符=初始化數據;

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

const 類型標識符 *指針變量標識符;

類型標識符 const *指針變量標識符;

應用舉例:

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

void Selection(int *pa,int n);

//選擇排序。非“只讀”函數。

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

//const常量數組。

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

//非const常量數組。

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

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

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

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

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

如果const修飾的是指針指向的數據,那么它是在修飾在修飾函數,表示以該指針為參量的函數對該指針指向的數據是“只讀”的,該指針就是指向const常量的指針。對這樣的指針,有下面幾點需要認識:

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

const int *pa=b;

但是const常量型數據只能傳址給代表函數“只讀”性質的指向const常量的指針。可以把帶有指向const常量指針參量的函數比作一個認真辦事的人,什么樣的事情交給他,他都認真處理,而一件需要認真處理的事情一定要交給他。

② 因為指向const常量的指針表示的是函數的“只讀”性質,而不是指針本身的數據的只讀性質,所以與const常量不同,這種指針不必初始化。例如:

const int *pa=b;

可以分解為

const int *pa;

pa=b;

而且對它本身的數據可以改變,例如:

const int *pa;

pa=b;//指向數組b

pa=a;//又指向數組a

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

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

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

{

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

……

}

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

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

類型標識符 *const指針標識符=初始化數據;

例如:

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

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

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

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

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

因為const常量指針不是限定函數,對它指向的數據可以修改,所以不能把const常量型數據的地址賦給const常量指針。例如:

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

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

3運算符函數與指針

3.1運算符函數

運算符處理的對象如果是語言內置基本類型(整型、浮點型、字符型等),它的意義是內定的。如果是用戶定義的結構,意義就是待定的。以結構數組的查找Find為例:

struct Student//用戶結構

{

long ID;double g;//ID表示學號,g表示成績。

};

typedef Student Type;//形式數據類型Type。

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

{

for(int i=0;i

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

return(i);

return(-1);

}

陰影部分中的關系運算對象是結構,系統無法確定是比較學號還是比較成績。我們可以進入函數體直接改造:

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

不過這是權宜之計。結構各式各樣,數組的處理程序數不勝數,都一一改造嗎?這顯然不符合代碼的復用性要求。解決這個問題的方法是運算符重載。

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

以比較運算符“==”為例,首先把該運算符從觀念上看作一個函數:

int operator==(int,int);

于是兩個整數的比較運算表達式

x==y

被看作運算符函數的調用

Operator== (x,y

然后反過來,重載運算符函數operator==:

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

{

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

}

重載之后,運算符“==”的處理對象就增加了結構Student。具體的執行過程是,編譯器如果發現內部無法解釋的運算符處理,就會去尋找重載的運算符函數,找到之后,調用這個函數。例如,函數Find中的表達式

pa[i]==item

被編譯器替換成

operator==(pa[i],item)

運算符重載是函數的一種調用形式。對用戶自定義類型重載的運算符運算,可以等價地表示為運算符函數的調用,但是內部基本類型的運算符運算是內定的,不能實際的替換成運算符函數的調用形式,例如,不能把表達式5==6替換為operator==(5,6)。

3.2引用

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

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

{

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

}

因為這樣的運算符函數與浮點型基本運算沖突,使編譯器失去了檢錯能力。

一個可行的方法是,首先創建一個用戶結構類型表示雙浮點型:

struct DOUBLE//創建一個用戶結構類型表示雙浮點型

{

double f;

};

然后運算符重載如下:

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

{

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

}

應用舉例:

DOUBLE x={13.1};

double y=4.5;

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

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

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

{

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

}

我們可以做如下改進,使某一個參量不是內置類型:

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

{

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

}

于是有:

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

return(i);

但是,表達式“pa[i]==item”把運算符的簡潔形式“pa[i]==item”破壞了,而且第1個參量仍然是值傳遞。

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

類型標識符 引用=被引用的變量;

舉例說明:

int x=5;

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

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

引用的實質是指針。在內部,引用是指針,而且它必須初始化,取得被引用變量的地址,初始化值不能改變。語句int y=x在內部相當于int* y=x。

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

y=6;//內部相當于*y=6;

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

在內部,引用相當于const常量指針。在外部,引用與const常量指針不同,對它本身既不能取址也不能取值,因為它是被引用的變量的別名,例如,y表示的是x的地址,而不是y指針的地址;y的值是x的值,而不是y指針的值即x的地址。

可以用下面一個簡單方法來驗證“引用的實質是指針”。我們知道,一個函數的自動局部變量地址不能是函數返回值,因為函數調用之后,其自動局部變量的生命周期結束,空間被撤消,返回它的地址是沒有意義的。例如:

int* Func2(void)

{

int x=5;

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

}

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

int Func2(void)

{

int x=5;

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

}

把運算符函數的參量設為引用型,問題就得到了解決:

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

{

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

}

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的實質是指針,相當于const常量指針,而運算符函數operator==是“只讀”的,它的指針參量應該是指向const常量的指針,所以a和b的實質是指向const常量的const常量指針,而它們的名稱是const常量型引用。

3.3基本類型運算符中的引用

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

(x=y)=z

執行過程是,y的值給x,z的值給x,結果是x和z的值相等。

從概念上用復合運算符函數表示為:

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

這不僅要求運算符函數operator=的第1個參量是引用,而且返回值也是第1個參量的引用。為了理解,我們以用戶定義的結構Student為例,重載賦值運算符:

Student operator= (Student a,const Student b)

{

a.ID=b.ID;

a.g=b.g;

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

}

由此說明,引用是指針發展的一種較高級的形式。運算符重載是引用產生的必要性,而基本數據類型運算符包含著它產生的可能性。

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

(x=3)=4

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

4通用算法與指針

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

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

有關具體內容將在后期引入C++后進一步討論。

5小結

存儲和處理是程序設計的基本矛盾,處理的不斷復雜,推動了這個矛盾的不斷發展,進而也推動了程序語言的不斷發展。地址、指針、指向const常量的指針、引用和迭代器是處理和存儲的“媒介”在程序語言發展中的一系列進化。

參考文獻

[1] 王立柱.C/C++與數據結構(第3版上)[M]. 北京: 清華大學出版社,2008. 215.

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

主站蜘蛛池模板: 亚洲日韩在线满18点击进入| 国产成人免费| 伊人狠狠丁香婷婷综合色| 黄色网站不卡无码| 亚洲国产精品人久久电影| 欧美成人一区午夜福利在线| 亚州AV秘 一区二区三区| 久久女人网| 亚洲女同欧美在线| 欧美午夜视频在线| 国产精品不卡片视频免费观看| 亚洲午夜国产精品无卡| 久久一级电影| 国产交换配偶在线视频| 久久77777| 操操操综合网| 亚洲视频免费在线看| 国产一级在线观看www色 | 在线毛片免费| 91九色国产porny| 亚洲香蕉久久| 色135综合网| 91无码视频在线观看| 亚洲国产中文欧美在线人成大黄瓜| 天天色天天综合网| 国产亚洲高清视频| 91久久国产综合精品女同我| 毛片网站在线看| 国产天天色| 99视频在线免费| 亚洲性网站| 一级毛片无毒不卡直接观看| 国内精品久久久久久久久久影视 | 欧美日本中文| 国产69精品久久久久孕妇大杂乱| 中文字幕欧美日韩| 超薄丝袜足j国产在线视频| 国产在线一区视频| 国产91丝袜在线播放动漫 | 国产成人精品一区二区不卡| 久青草免费在线视频| 国产三级国产精品国产普男人| 91精品国产丝袜| 国产精品一线天| 尤物在线观看乱码| 久久精品一卡日本电影| 男女男精品视频| 在线中文字幕日韩| 亚洲无线一二三四区男男| 国产精品嫩草影院视频| 一级毛片免费播放视频| 国产精品网址在线观看你懂的| 国产新AV天堂| 亚洲无码视频图片| 制服丝袜在线视频香蕉| 91在线无码精品秘九色APP| 亚洲制服丝袜第一页| AV熟女乱| 2020最新国产精品视频| 日本不卡在线| 国产欧美在线| 婷婷午夜天| 国产成人精品亚洲77美色| 色老二精品视频在线观看| 国产香蕉在线| aa级毛片毛片免费观看久| 99精品国产电影| 久久伊伊香蕉综合精品| 欧美日韩久久综合| 色综合手机在线| a毛片免费看| 91麻豆国产在线| 亚洲V日韩V无码一区二区| 91精品啪在线观看国产60岁 | 国产视频只有无码精品| 亚洲AV无码精品无码久久蜜桃| 国产精品亚洲欧美日韩久久| 日韩无码视频专区| 热久久国产| 亚欧美国产综合| 欧美成人精品欧美一级乱黄| 久久人妻xunleige无码|