C++ 函數(shù)

第五章(循環(huán)和關(guān)系表達式)和第六章(分支語句和邏輯運算符)直接跳過浇冰,所有語言都一樣的浆西,if/else/switch/while/for這種曾掂。直接進入第七章(函數(shù))。

1.函數(shù)原型

以下是函數(shù)原型的例子

void cheers(int);//cheers方法的函數(shù)原型

int main()
{
    using namespace std;
    cheers(5);
    return 0;
}

//函數(shù)的實際實現(xiàn)
void cheers(int n)
{
    using namespace std;
    for(int i=0;i<n;i++){
        cout << "Cheers! ";
    }
    cout << endl;
}

避免使用函數(shù)原型的唯一方法是壁顶,在首次使用函數(shù)之前定義它珠洗。函數(shù)原型不要求提供變量名,有類型列表就足夠了若专。
函數(shù)原型的作用:

  • 確保編譯器正確處理函數(shù)返回值许蓖;
  • 編譯器檢查使用的參數(shù)數(shù)目是否正確;
  • 編譯器檢查使用的參數(shù)類型是否正確调衰。如果不正確膊爪,則轉(zhuǎn)換為正確的類型(如果可能的話)。
2.函數(shù)參數(shù)和按值傳遞

舉個栗子

double volume = cube(side);

cube的原型如下:

double cube(double x);

被調(diào)用時嚎莉,該函數(shù)將創(chuàng)建一個新的名為x的double變量米酬,并將其初始化。這樣cube執(zhí)行的操作將不會影響原數(shù)據(jù)趋箩,因為cube()使用的是side的副本赃额,而不是原來的數(shù)據(jù)(C/C+新手的話,一定要注意這塊)阁簸。

3.函數(shù)和數(shù)組

還是先舉個栗子

int sum_arr(int arr[],int n);// n是arr的size

表面上看arr是個數(shù)組爬早,但是實際上arr是個指針哼丈。在C++中启妹,當(dāng)且僅當(dāng)用于函數(shù)頭或函數(shù)原型中,int *arr 和 int arr[]的含義是相同的醉旦。它們都意味著arr是一個int指針饶米。這塊是個知識點,面試題經(jīng)常會問车胡,數(shù)組在函數(shù)參數(shù)時是退化為指針的檬输。不明白的同學(xué)可以嘗試理解下下面的代碼

#include <iostream>

void sizeOfArray(int arr[])
{
    //函數(shù)中,arr從數(shù)組退化成指針
    using namespace std;
    cout << "in func arr size:" << sizeof(arr) << endl;
}

int main() {
    using namespace std;
    int arr[10];
    cout << "arr size:" << sizeof(arr) << endl;//輸出的值為sizeof(int)*10
    sizeOfArray(arr);//輸出的值為指針所占的字節(jié)數(shù)匈棘,64位mac為8
    return 0;
}

將數(shù)組地址作為參數(shù)的好處是可以節(jié)省復(fù)制整個數(shù)組所需的時間和內(nèi)存丧慈。如果數(shù)組很大,使用拷貝的系統(tǒng)開銷將非常大主卫;程序不僅需要更多的計算機內(nèi)存逃默,還需要花時間復(fù)制大塊的數(shù)據(jù)。壞處是使用原數(shù)據(jù)增加了破壞數(shù)據(jù)的風(fēng)險簇搅,可以使用const保護數(shù)組完域,如下:

void show_array(const int arr[],int size);//函數(shù)原型

如果嘗試在show_array的實現(xiàn)中嘗試修改arr,編譯器會報錯瘩将,如下

AF676F8B-B7F2-421C-8C1D-6CB5579F6063.png

將const用于指針有一些很微妙的地方吟税“及遥可以用兩種不同的方式將const用于指針。第一種方法是讓指針指向一個常量對象肠仪,這樣可以防止使用該指針來修改所指向的值肖抱,第二種方法是將指針本身聲明為常量,這樣可以防止改變指針指向的位置藤韵。舉個栗子:

int age = 39;
const int *pt = &age;//一個指針虐沥,指向的是const int
*pt = 1;//不可以,因為指針指向的是const int
age = 20;//可以泽艘,因為age本身只是int欲险,是可變的

const float g_earth = 9.80;
const float *pe = &g_earth;//可以,常量

const float g_moon = 9.99;
float * pm = &g_moon;//不可以匹涮,C++禁止這種情況

如果將指針指向指針天试,也是類似的規(guī)則,C++不允許如下的情況

const int **pp;
int *p;
pp = &p;//不可以然低,編譯器報錯

結(jié)論:如果數(shù)據(jù)類型本身并不是指針喜每,則可以將const數(shù)據(jù)或非const數(shù)據(jù)的地址賦給const的指針,但只能將非const數(shù)據(jù)的地址賦給const指針雳攘。

并且建議盡可能將指針參數(shù)聲明為指向常量數(shù)據(jù)的指針带兜,理由如下:

  • 可以避免由于無意間修改數(shù)據(jù)而導(dǎo)致的編程錯誤;
  • 使用const使得函數(shù)能夠處理const和非const實參吨灭,否則將只能接受非const數(shù)據(jù)刚照。
    再介紹下常量指針,如下
int a = 3;
int b = 4;
int * const pt = &a;//可以
pt = &b;//不可以

pt是一個常量指針喧兄,初始化后將不能再修改指向位置无畔。

4.函數(shù)和二維數(shù)組

考慮如下情況:

int data[3][4];
int total = sum(data,3);

sum的函數(shù)原型應(yīng)該是啥樣?答案是

int sum(int (*a)[4],int size);

這里要注意的是吠冤,多維數(shù)組的話浑彰,是有第一維會退化成指針,后面的維度都是還是數(shù)組拯辙。并且第一個參數(shù)應(yīng)該是int (*a)[4]郭变,而不是int *a[4],因為int *a[4]表示一個由4個指向int的指針組成的數(shù)組。sum的另一種可讀性更強的原型是

int sum(int a[][4],int size);

如果對于上面的描述理解不上去的涯保,可以結(jié)合下面的代碼感受下

int val = 20;
int valArray[3][4];

int *ptArray[4];//指針數(shù)組诉濒,也可以這么理解 (int *)ptArray[4],ptArray是一個數(shù)組,每個元素 int*
int *pt = &val;
ptArray[0] = pt;

int (*arrArray)[4];//arrArray是個指針遭赂,指針指向的每個元素是個int [4]類型
arrArray = valArray;//可以
ptArray = valArray;//不可以循诉, 編譯器報錯,類型不對
5.函數(shù)和C-風(fēng)格字符串

先溫習(xí)下C-風(fēng)格字符串撇他,表示的方式有三種:

  • char數(shù)組茄猫;
  • 用引號括起的字符串常量(也稱字符串字面值)狈蚤;
  • 被設(shè)置為字符串的地址的char指針。
    上述其實說的都是char指針(char*)划纽,因此函數(shù)原型參數(shù)都為如下
void processCStr(char *); 

C風(fēng)格字符串與常規(guī)char數(shù)組的一種重要區(qū)別是字符串有內(nèi)置的結(jié)束字符脆侮。這意味著不必將字符串長度作為參數(shù)傳遞給函數(shù),函數(shù)可以使用循環(huán)檢查字符串中的每個字符直到遇到結(jié)尾的空字符勇劣。
返回的字符串的方式如下:

char* returnStr(){
    char *s = "string";
    return s;
}

int main() {
    using namespace std;
    cout << "result:" << returnStr() << endl;
    return 0;
}
6.函數(shù)和結(jié)構(gòu)體

結(jié)構(gòu)體和普通變量類似靖避,函數(shù)都將創(chuàng)建參數(shù)對應(yīng)的副本,函數(shù)內(nèi)操作的其實是結(jié)構(gòu)體變量的副本比默,所以在結(jié)構(gòu)體變量包含的數(shù)據(jù)較多時幻捏,會有性能問題。有兩種方式可以提高效率命咐,第一種是傳遞結(jié)構(gòu)體的指針篡九,第二種是傳遞結(jié)構(gòu)體的引用(關(guān)于引用會在下一章講解),簡單舉個栗子:

#include <iostream>

struct Person {
    int age;
    char *name;
};

//在函數(shù)內(nèi)的操作將不影響原值
void processStruct1(Person p) {
    p.name = "mrlee1";
    p.age = 20;
}

void processStruct2(Person *p) {
    p->name = "mrlee2";
    p->age = 21;
}

void processStruct3(Person &p) {
    p.name = "mrlee3";
    p.age = 22;
}

void printPerson(Person p) {
    using namespace std;
    cout << "age:" << p.age << ",name:" << p.name << endl;
}

int main() {
    using namespace std;
    Person originPerson = {18, "mrlee"};

    //按值傳遞
    processStruct1(originPerson);
    printPerson(originPerson);//實際打印age:18,name:mrlee,沒有變化
    //指針傳遞
    processStruct2(&originPerson);
    printPerson(originPerson);//實際打印age:21,name:mrlee2
    //引用傳遞
    processStruct3(originPerson);
    printPerson(originPerson);//實際打印age:22,name:mrlee3

    return 0;
}
7.函數(shù)和string對象

雖然C風(fēng)格字符串和string對象的用途幾乎相同醋奠,但與數(shù)組相比榛臼,string對象與結(jié)構(gòu)體更相似。例如窜司,可以將一個結(jié)構(gòu)體賦給另一個結(jié)構(gòu)體沛善,也可以將一個對象賦給結(jié)構(gòu)體。如果需要多個字符串塞祈,可以聲明一個string對象數(shù)組金刁,而且不是二維char數(shù)組。舉個栗子:

#include <iostream>
#include <sstream>


using namespace std;

void processSting(const std::string strings[], int size) {
    for (int i = 0; i < size; i++) {
        cout << "string[" << i << "]:" << strings[i] << endl;
    }
}

/**
 * 這個比較尷尬织咧,因為C++里int轉(zhuǎn)string還是比較麻煩胀葱,目前先這么寫了
 * @param n
 * @return
 */
string intToString(int n) {
    stringstream stream;
    stream << n;
    return stream.str();
}


int main() {
    const int size = 5;
    string strings[size];
    for (int i = 0; i < size; i++) {
        strings[i] = "mrlee" + intToString(i + 1);
    }
    processSting(strings, size);
    return 0;
}
8.函數(shù)與array對象

沒啥好說的漠秋,看栗子吧:

#include <iostream>
#include <sstream>
#include <array>
#include <string>


using namespace std;

const int size = 4;

/**
 * 這個比較尷尬笙蒙,因為C++里int轉(zhuǎn)string還是比較麻煩,目前先這么寫了
 * @param n
 * @return
 */
string intToString(int n) {
    stringstream stream;
    stream << n;
    return stream.str();
}

//按值傳遞庆锦,函數(shù)處理的是原始對象的副本
void wrongModifyArray(array<string, size> stringArray) {
    for (int i = 0; i < stringArray.size(); i++) {
        stringArray[i] = "modified1";
    }
}

//按指針傳遞
void rightModifyArray(array<string, size> *stringArray) {
    for (int i = 0; i < (*stringArray).size(); i++) {
        (*stringArray)[i] = "modified" + intToString(i);
    }
}

//按引用傳遞
void rightModifyArray2(array<string, size> &stringArray) {
    for (int i = 0; i < stringArray.size(); i++) {
        stringArray[i] = "modified2" + intToString(i);
    }
}

void printArray(array<string, size> stringArray) {
    for (int i = 0; i < stringArray.size(); i++) {
        cout << "string" << i << ":" << stringArray[i] << endl;
    }
    cout << endl;
}


int main() {
    array<string, size> originStringArray = {
            "string1", "string2", "string3", "string4"
    };
    printArray(originStringArray);

    wrongModifyArray(originStringArray);
    printArray(originStringArray);

    rightModifyArray(&originStringArray);
    printArray(originStringArray);

    rightModifyArray2(originStringArray);
    printArray(originStringArray);

    return 0;
}

打印結(jié)果如下

string0:string1
string1:string2
string2:string3
string3:string4

string0:string1
string1:string2
string2:string3
string3:string4

string0:modified0
string1:modified1
string2:modified2
string3:modified3

string0:modified20
string1:modified21
string2:modified22
string3:modified23
9.函數(shù)指針

函數(shù)這個話題比較大捅位,這里只是簡單點一下。
與數(shù)據(jù)項相似搂抒,函數(shù)也有地址艇搀。函數(shù)的地址是存儲其機器語言代碼的內(nèi)存的開始地址。還是看個栗子吧:

void func(string name) {
    cout << "hello " << name << endl;
}

int main() {
    //聲明函數(shù)指針
    void (*funcPt)(string);
    
    //獲取函數(shù)的地址,就是函數(shù)名其實
    funcPt = func;
    
    //使用指針調(diào)用函數(shù)
    funcPt("mr.lee");
    
    //下面方式也可以求晶,個人傾向于下面這種焰雕,雖然寫的對了,但是表示的比較明確
    (*funcPt)("mr.lau");
    return 0;
}

通常芳杏,要聲明指向特定類型的函數(shù)的指針矩屁,可以首先編寫這個函數(shù)的原型辟宗,然后用形如(*pt)替換函數(shù)名即可。

下面解釋一個稍微復(fù)雜的栗子:

const double *(*pa[3])(const double *,int) = {f1,f2,f3};

在解釋這個復(fù)雜的栗子之前首先回顧一點東西

int *a[3];//a是一個數(shù)組吝秕,每個元素是int *;
int (*b)[3];//b是一個指針泊脐,指針指向的每個元素都是int[3]

f1是什么類型的?
來逐步解釋烁峭,首先運算符[]優(yōu)先級高于,因此pa[3]表示p3是一個數(shù)組容客,這個數(shù)組包含三個元素,每個元素是指針類型约郁。那是什么指針類型呢?是一個返回const double *,參數(shù)是const double *和int的函數(shù)缩挑,所以f1的聲明如下

const double * f1(const double *,int)

上面的代碼比較冗長,可以考慮使用typedef簡化鬓梅,先看下簡單的typedef如何使用

typedef double real;
int main() {
    real a = 5.4;
    return 0;
}

再看如何簡化上面的函數(shù)指針

typedef double * (*p_fun)(const double *,int);//聲明別名
const double * f1(const double *,int);//聲明函數(shù)原型
p_fun func_pt = f1;//獲取指針
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末调煎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子己肮,更是在濱河造成了極大的恐慌士袄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎僻,死亡現(xiàn)場離奇詭異娄柳,居然都是意外死亡,警方通過查閱死者的電腦和手機艘绍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門赤拒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诱鞠,你說我怎么就攤上這事挎挖。” “怎么了航夺?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵蕉朵,是天一觀的道長。 經(jīng)常有香客問我阳掐,道長始衅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任缭保,我火速辦了婚禮汛闸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艺骂。我一直安慰自己诸老,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布钳恕。 她就那樣靜靜地躺著别伏,像睡著了一般吮廉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畸肆,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天宦芦,我揣著相機與錄音,去河邊找鬼轴脐。 笑死调卑,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的大咱。 我是一名探鬼主播恬涧,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碴巾!你這毒婦竟也來了溯捆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤厦瓢,失蹤者是張志新(化名)和其女友劉穎提揍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煮仇,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡劳跃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了浙垫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刨仑。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夹姥,靈堂內(nèi)的尸體忽然破棺而出杉武,到底是詐尸還是另有隱情,我是刑警寧澤辙售,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布轻抱,位于F島的核電站,受9級特大地震影響圾亏,放射性物質(zhì)發(fā)生泄漏十拣。R本人自食惡果不足惜封拧,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一志鹃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泽西,春花似錦曹铃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秘血。三九已至,卻和暖如春评甜,著一層夾襖步出監(jiān)牢的瞬間灰粮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工忍坷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粘舟,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓佩研,卻偏偏與公主長得像柑肴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子旬薯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345