咱們知道,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; }