国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

揭開C/C++中數組形參的迷霧

2019-11-17 05:34:54
字體:
來源:轉載
供稿:網友
楔子
  去年,周星星大哥曾經在VCKBASE/C++論壇發表過一篇文章“數組引用"以避免"數組降階”,當時我不能深入理解這種用法的含義;時隔一年,我的知識有幾經錘煉,終于對此文章漸有所悟,所以把吾所知作想具體道來,竟也成了一篇文章。希望本文能對新手有所啟迪,同時也希望大家發現本文中的疏漏之處后不吝留言指教。
  故事起源于周星星大哥給出的兩個Demo,為了節省地方,我把兩個Demo合二為一,也能說明同樣的問題:#include <iostream>using namespace std;void Foo1(int arr[100]){ cout << "pass by pointer: " << sizeof(arr) << endl;}void Foo2(int (&arr)[100]){ cout << "pass by reference: " << sizeof(arr) << endl;}void main(){ int a[100]; cout << "In main function : " << sizeof(a) << endl; Foo1(a); Foo2(a); }其運行結果如下:In main function : 400pass by pointer: 4pass by reference: 400   這段代碼說明了,假如數組形參是數組名形式(或者指針形式,下文討論)時,使用sizeof運算符,將得不到原來數組的長度;假如用傳遞原數組引用的方法,則沒有問題。
  這段代碼的確很難理解,因為這短短的十幾行涉及到了形參與實參的關系、數組名和指針的關系、引用的意義、聲名和表達式的關系這4大類問題,只要有1條理解不透、或者理解不正確,就理解不透上面的這段代碼。本文也就從這4個問題入手,把這4個問題首先解決掉,然后再探討上面的這段代碼。雖然這樣看來很是繁復,但是我認為從根上入手來理解、學習,是條似遠實近的道路。

一、函數形參和實參的關系 void Foo(int a);Foo(10);   這里的a叫做形式參數(parameter),簡稱形參;這里的10叫做實際參數(argument),簡稱實參。形參和式參之間是什么關系呢?他們是賦值的關系,也就是說:把實參傳遞給形參的過程,可以看作是把實參賦值給形參的過程。上面的例子中,實參10傳遞給形參a,就相當于a=10;這個賦值的過程。(因為數據類型多的很,無法舉例子舉全面,所以這里就不舉例子了;假如覺得不好理解,就在vc中寫個sample調試一下各種數據類型的情況,你就能夠驗證這個結論了。)

二、數組名和指針的關系

  這個問題是個歷史性的問題了,在C語言中,數組名是當作指針來處理的。更確切的說,數組名就是指向數組首元素地址的指針,數組索引就是距數組首元素地址的偏移量。理解這一點很重要,很多數組應用的問題就是有此而起的。這也就是為什么C語言中的數組是從0開始計數,因為這樣它的索引就比較好對應到偏移量上。在C語言中,編譯過程中碰到有數組名的表達式,都會把數組名替換成指針來處理;編譯器甚至無法區分a[4]和4[a]的區別!*2 但是下面這一點需要注重:int a[100];int *b;   這兩者并不等價,第一句話聲明了數組a,并定義了這個數組,它有100個int型元素,sizeof(a)將得到整個數組所占的內存大小,是400;第二句話只是聲明并定義了一個int型的指針,sizeof(b)將得到這個指針所占的內存大小,是4。所以說,雖然數組名在表達式中一般會當作指針來處理,但是數組名和指針還是有差距的,最起碼有a==&a[0]但是sizeof(a)!=sizeof(a[0])。
  并且在ANSI C標準中,也明文規定:在函數參數的聲明中,數組名北邊一起當作指向該數組第一個元素的指針。所以,下面的幾種書寫形式是等效的:
void Foo1(int arr[100]){}void Foo2(int arr[]){}void Foo3(int *arr){}C++盡可能的全面兼容C語言,所以這一部分的語法相同。 三、引用的意義

  “引用“是C++中引進的概念,C語言中沒有。它的目的在于,在某些方面取代指針。假如你認為引用和指針并無大不同,肯定會為指針報不平,頗有一種“即生亮何生瑜”的感慨;但是,引用確實有新的特色,也確實在很多地方的表現和指針有所不同,本文就是一例。使用引用,我們要把握這它最最最重要的一點,這也是它和指針最大的區別:引用一經定義,就和被它引用的變量緊緊地結合在一起,再不分開,對引用的任何操作都反映在它引用的變量上;而指針,只是訪問它指向變量的另一種方式,兩者雖有聯系,但是并不像引用那樣密不可分。:)
#include <iostream>using namespace std;void main(){ int a = 10; int & a_ref = a; int b = 20;  // 定義引用時就要初始化,說明引用跟它指向的元素密不可分 //int & b_ref ; // error C2530: ''b_ref'' : references must be initialized int & b_ref = b; int * p; int * q; //下面的結果證實了:引用一經定義,就不能再指向其他目標;  //把一個引用b_ref賦值給另一個引用a_ref,其實就是把b賦值給了a. cout << a_ref << " " << b_ref << endl; a_ref = b_ref; cout << a_ref << " " << b_ref << endl;
cout << a << " " << b << endl; cout << endl; //即使對一個引用a_ref取地址,取得也是a的地址。已經“惡鬼附體”了:) p = &a; q = &a_ref; cout << p << " " << q << endl; cout << endl; //下面這段代碼展示了指針與引用的不同 p = &a; q = &b; cout << p << " "<< q << endl; p = q; cout << p << " "<< q << endl; cout << endl; system("pause");}下面是運行的結果,以供參考:10 2020 2020 200012FED4 0012FED40012FED4 0012FEBC0012FEBC 0012FEBC 四、聲明和表達式的關系

  這里想說明的是,分析一個聲明可以把它看作一個表達式,按照表達式中的運算符優先級順序來聲明。比如int (&arr)[100],你首先要找到聲明器arr,那么&arr說明arr是一個引用。什么引用呢?在看括號外面,[]說明了這一個數組,100說明這個數組有100個元素,前面的int說明了這個數組的每個元素都是int型的。所以,這個聲明的意思就是:arr就是指向具有100個int型元素的數組的引用。假如你覺得這種理解很晦澀,那你就不妨用typedef來簡化聲明中的復雜的運算符優先級關系,比如下面的形式就很好理解,其效果是和最初的那個例子是一樣的:
#include <iostream>using namespace std;typedef int INTARR[100]; //這個,這個...也可以用表達式來理解,有點“GNU is not UNIX“的味道是吧?void Foo(INTARR &arr) //noh,這樣看就很明白了,就是傳了個引用進去{ cout << "pass by reference: " << sizeof(arr) << endl;}void main(){ INTARR a; //用類型別名來定義a INTARR &a_ref=a; //用類型別名來定義引用a_ref cout << "In main function : " << sizeof(a) << endl; Foo(a); system("pause");} 大結局

  吐沫星亂飛了半天,大家感覺還好吧,快結束了,大家再忍耐一下??纯聪旅孢@段程序:
#include <iostream>using namespace std;void main(){ int a[100]; int * pa = a; int (&a_ref)[100] = a; cout << sizeof(a) << endl; cout << sizeof(pa) << endl; cout << sizeof(a_ref) << endl; system("pause");}   怎么樣,是不是對輸出結果感到很自然呢?假如是,那就好辦了。我總結一下就下課哈!^_^ 數組名在表達式中,往往被當作是指向首元素a[0]地址的指針,但是在sizeof(a)中,返回的結果是數組a占用內存的大??;pa是指向a的指針,他也指向a[0],但是sizeof(pa)中,返回結果是pa這個指針所占內存空間的大小,之所以這樣,因為pa這個指針和數組a的結合不夠緊密,屬于訪問數組a的第二被選方案;a_ref這個引用,就是對數組a的引用,就像“惡鬼附體”一樣,一旦附體附上了,你怎么也甩不掉它,對它的任何操作,全部都反映在a上。在看本文最初的那個例子,比這個例子所增加的操作就是函數實參到形參的傳遞,我們在上面說過了,從實參到形參的傳遞可以看作是把實參賦值給形參。所以本文最初的那個例子,其實際的操作過程就和本文最后的這個例子是一樣的。所以,并非函數把數組給“降階”了,而是它原原本本就該這樣,千萬不必希奇。 :p
  意猶未盡,在PS一段:在C語言中,沒有引用,是怎么解決這種問題呢。下面是常用的幾種作法:
 
  1. 傳遞數組的時候,在增加一個參數,用來記錄數組的元素個數或者長度。main(int argc, char ** args)就是這種做法;這種方法還可以防止溢出,安全性比較高。
  2. 在數組的最后一個有效元素后面作一個標志,指明數組已經結束了。C語言中用char數組表

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 上饶市| 华池县| 方山县| 特克斯县| 宁海县| 容城县| 南雄市| 碌曲县| 得荣县| 巴马| 多伦县| 安阳市| 浦县| 牡丹江市| 宁海县| 九龙坡区| 沙坪坝区| 庄河市| 读书| 灵丘县| 临武县| 辽阳市| 鹤庆县| 浙江省| 包头市| 奉化市| 商河县| 河北省| 浙江省| 察隅县| 乐业县| 封开县| 海原县| 行唐县| 宜都市| 藁城市| 长治市| 凌海市| 吴桥县| 崇礼县| 海门市|