第7講——函數初步

咱們知道,C++自帶了一個包含函數的大型庫(標準ANSI庫加上多個C++類),但這並不能知足咱們的需求,咱們須要編寫本身的函數。但咱們在編寫函數時爲了提升編程效率,可更深刻地學習STL和BOOST C++提供的功能。程序員

 

咱們先學習一下庫函數,它是已經定義和編譯好的函數,同時可使用標準庫頭文件提供其原型,所以只需正確地調用這種函數便可。例如,標準C庫中有一個strlen()函數,相關的標準頭文件cstring包含了strlen()和其餘一些與字符串相關的函數的原型。這些預備工做使程序員可以在程序中隨意使用strlen函數。編程

 

因此,建立本身的函數時,須要咱們自行處理這3個方面——定義、提供原型和調用。數組

 

咱們知道函數分爲:有返回值的函數和沒有返回值的函數。cookie

對於有返回值的函數,C++對於返回值的類型有必定的限制:不能是數組,但能夠是其餘任何類型——整數、浮點數、指針、結構和對象。app

有趣的是,雖然不能直接返回數組,但能夠將數組做爲結構或對象組成部分來返回。函數

下面咱們來討論函數是如何處理數組的。學習

若要設計一個對數組中元素求和的函數,那麼這個函數須要知道對哪一個數組進行累計,所以咱們須要將數組名做爲參數傳遞給它。爲使函數通用,而不限於特定長度的數組,還須要傳遞數組長度。下面咱們來看一看函數頭及其其餘部分:spa

int sum_arr(int arr[],int n)   //arr是數組名,n是數組長度

這看上去表示:方括號指出arr是一個數組,而方括號爲空則代表,能夠將任何長度的數組傳遞給該函數。設計

但實際狀況並不是如此:arr實際上並非數組,而是一個指針!好消息是,在編寫函數的其他部分時,能夠將arr看做是數組。指針

下面咱們來研究研究函數如何使用指針來處理數組:

咱們知道,C++將數組名視爲指針。以前咱們學過C++將數組名解釋爲第一個元素的地址:cookies == &cookies[0] 。可是咱們應該要知道的是:①數組聲明使用數組名來標記存儲位置;②對數組名使用sizeof將獲得整個數組的長度(以字節爲單位);③將地址運算符&用於數組名時,將返回整個數組的地址。

當咱們在調用函數中如此調用數組時:int sum = sum_arr(cookies, ArSize); (cookies是數組名),根據C++規則,cookies是其第一個元素的地址,所以函數傳遞的是地址。而因爲數組的元素的類型爲int,所以cookies的類型必須是int指針,即int*。這代表,正確的函數頭應該是這樣的:

int sum_arr(int *arr,int n)    //arr是數組名

其中,用int *arr替換了int arr[] 。這證實兩個函數頭都是正確的,由於在C++中,當(且僅當)用於函數頭或函數原型中,int *arr和int arr[]的含義纔是相同的。它們都意味着arr是一個int指針。然而,數組表示法(int arr[])提醒用戶,arr不只指向int,還指向int數組的第一個int。

 

有一點須要記住,不一樣於傳遞常規變量(須要新建變量存儲值),傳遞數組時,函數將使用原來的數組,這將能夠節省複製整個數組所需的時間和內存。

 

下面,咱們來討論函數與結構。

相比於數組,涉及到函數時,結構變量的行爲更接近於基本的單值變量。也就是說,與數組不一樣,結構將其數據組合成單個實體或數據對象,該實體被視爲一個總體。

那麼,咱們給出結構在函數中的特性:①能夠按值傳遞結構,就像普通變量那樣,同時,函數將使用結構的副本;②函數能夠返回結構,與數組名就是第一個元素的地址不一樣的是,結構名只是結構的名稱,要得到結構的地址必須使用地址運算符&。

使用結構編程時,最直接的方式是像處理基本類型那樣處理結構。也就是說,將結構做爲參數傳遞,並在須要時將結構用做返回值使用。因爲按值傳遞結構可能因結構過大而耗費大量內存,因此許多C程序員傾向於傳遞結構的地址,而後使用指針來訪問結構的內容,然而C++提供了第三種選擇——按引用傳遞。

【按值傳遞】

struct travel_time{
	int hours, mins;
};

travel_time sum(travel_time t1, travel_time t2)	//返回類型爲結構類型 
{
	...
}

int main()
{
	travel_time day1 = {5, 45};
	travel_time day2 = {4, 55};
	travel_time trip = sum(day1, day2);		//結構變量做爲參數 
}

在這裏,結構體類型travel_time就像是一個標準的類型名,可被用來聲明變量、函數的返回類型和函數的參數類型。

【按地址傳遞】

與傳值操做不一樣的是,這能讓函數對原始結構進行操做,而不是結構副本。

傳遞結構的地址而不是整個結構以節省時間和空間,此時須要使用指向結構的指針。

須要注意的是,若是咱們但願在函數中不修改結構,咱們應使用const修飾符。

 

關於函數和string對象,待往後學完string再來填坑。(p235)

 

忽然看到書上有函數指針,我靠,別逗我笑,這種低級書應該只是形式上地描寫一下。咱們也先大概談談吧。

看了一下,書上講的篇幅還挺多,我如今沒時間扯這個了,之後須要再看。(p241)

 

溫故而知新

【使用函數的3個步驟】

定義函數;提供原型;調用函數。

【編寫一個接受3個參數的函數:指向數組區間中第一個元素的指針、指向數組區間最後一個元素後面的指針,以及一個int值,並將數組中每個元素都設置爲該int值】

void set_array(int *begin, int *end, int value)
{
	while(begin != end){
		*begin = value;
		begin++;
	}
} 

//注:在函數內部移動指針,離開函數後,指針恢復到初始位置(應該是在函數內部建立了一個指針副本)。所以無需在函數內部新建立一個指針。

【爲何不對類型爲基本類型的函數參數使用const限定符】

答:由於函數在調用參數時,使用的是一個副本,而不是原來的數,所以不會修改做爲實參的基本類型的值。而指針不一樣,指針爲函數參數時,能夠經過修改直着,來修改指針所指向的值。

【C++程序可使用哪3種C-風格字符串格式】

答:字符串能夠被儲存在char數組中,可使用帶雙引號的字符串來表示(好比"abc",但這種沒法被修改),也能夠用指向字符串第一個字符的指針來表示。

【表達式*"pizza"的含義是什麼?"taco"[2]呢】

答:

*"pizza"的含義是:"pizza"是一個常量字符串,其名字表示爲指向其地址的指針(類型爲char*),對這個指針解除運算,是字符串的第一個字符——即p。*"pizze"的結果是:p

"taco"[2]的含義是:原理同上,這個常量字符串的第三個字符——是c。

以上答案存疑。

參考答案給的是:C++將字符串解釋爲指其第一個元素的地址,即p和t的地址,*給出第一個元素的值,[2]給第三個元素的值,因此分別是p和c。

【C++容許按值傳遞結構,也容許傳遞結構的地址。若是glitz是一個結構變量,如何按值傳遞他它?如何傳遞他的地址?這兩種方法有何利弊?】

答:

按值傳遞則是傳遞他的類型,而後glitz做爲參數進行傳遞。按地址傳遞則是參數使用結構指針。

按值傳遞的好處是不會修改原結構變量,按地址傳遞的好處正好是能夠在函數內修改原結構變量。

假如結構類型是abc,則聲明結構是abc glitz;

按值傳遞函數原型假如爲:void mmm(abc);

按地址傳遞函數原型假如爲:void mmm(abc*);

glitz做爲參數時,按值是glitz,按地址則爲&glitz。

補充:按值傳遞將自動保護原始數據,但這是以時間和內存爲代價的(由於要複製副本),按地址傳遞可節省內存和時間,但不能保護原始數據,解決辦法是使用const限定符。

【假設有以下結構聲明:struct applicant  {char name[30];  int credit_ratings[3];};

a。編寫一個函數,它將applicant結構做爲參數,並顯示該結構的內容。

b。編寫一個函數,他將applicant結構的地址做爲參數,並顯示該參數指向的結構的內容。】

void show_1(applicant m)
{
	cout<<m.name<<endl;
	for(int i=0;i<3;i++)
		cout<<m.credit_ratings[i]<<endl;
}

void show_2(applicant *m)
{
	cout<<m->name<<endl;
	for(int i=0;i<3;i++)
		cout<<(*m).credit_ratings[i]<<endl;
}
相關文章
相關標籤/搜索