C++基礎入門教程(八):函數指標_C 語言

來源:互聯網
上載者:User

最近事情比較多,其實並不忙,就是事情比較影響思緒,所以都沒心思寫文章了。
今天主要說說函數的一些基本情況吧,同時也解釋一下新手最容易迷糊的——什麼時候要用指標參數?

一、函數原型和函數定義

大家都知道,C++定義函數之前,還需要聲明函數原型,對於習慣Java等其他進階語言的朋友來說,真心覺得這很煩人。
如下代碼:

複製代碼 代碼如下:

// 聲明函數原型
void startGame(int param);
// 函數定義
void startGame(int param)
{
    // 各種邏輯
}

函數原型主要是給編譯器用的,在編譯的時候會通過函數原型來檢查函數傳回值、參數數量、參數類型等。
總而言之,方便編譯器,編譯器爽了,我們才能更爽。

但實際中,函數原型也方便我們快速理解某個類的功能。

這些很簡單,就不多嘮叨了。

二、const限定符與指標

之前也有簡單介紹過const,比如 const int num = 10; 那麼num就是常量,不可再次進行賦值操作了。
如果把const用在指標上呢?

複製代碼 代碼如下:

    int num = 10;
    const int *p = #
 
    // 編譯會報錯
    *p = 100;

如上代碼,編譯的時候就會報錯,因為指標p指向一個const int類型,這是一個常量。
所以*p的值是一個常量,不能被修改。

再來理一理,不然等會會混亂的:
1.p是一個指標
2.p指向一個記憶體位址,這個地址裡存放的是一個const int類型的值
3.*p代表是p指向的記憶體位址裡存放的那個值
4.所以,*p就是一個const int類型的值
5.綜上所述,*p不能再次被賦值。

這裡要區分p和*p,這是兩個概念,一個是指標,一個是指標指向的值。
p是可以被再次賦值的,但是*p是不能被賦值的。

三、函數的指標參數

先來看看下面的代碼:

複製代碼 代碼如下:

void notChangeNum(int num);
void changeNum(int* num);
int _tmain(int argc, _TCHAR* argv[])
{
    int num = 10;
  
    // 這個函數不會改變num的值
    notChangeNum(num);
    cout << num << endl;
    // 這個函數會改變num的值
    int* p = #
    changeNum(p);
    cout << num << endl;
    return 0;
}
void notChangeNum(int num)
{
    // 參數不是指標
    num = 999;
}
void changeNum(int* num)
{
    // 參數是指標
    *num = 999;
}

這裡有兩個函數,一個是普通參數(值傳遞),一個是指標參數(地址傳遞)。

第一個notChangeNum函數是不會改變num的值的,因為num傳遞給函數時,是拷貝了一份新的值,原來的num是不受影響的。

當離開notChangeNum函數後,函數的num參數會被釋放。

第二個changeNum函數的參數是指標,我們都知道,指標是指向某個記憶體位址的,所以,函數的參數指向的記憶體位址就是num的記憶體位址。

直接修改記憶體位址上的值,會影響原來的num,所以,離開changeNum函數後,num的值也會被改變,最終值是999.

這就是指標參數的作用,某些情況下,我們希望函數裡對參數的修改能夠真正產生影響。

四、為什麼要使用指標參數

為什麼要用指標作為參數呢?因為指標可以直接指向記憶體位址,可以直接在函數裡修改值,並且離開函數後仍然生效。
說是這麼說,但,肯定還有人會迷糊,為什麼呢?為什麼要這樣呢?

比如,我們的函數參數是某個類:

複製代碼 代碼如下:

void play(Sprite* sp) {
}

Sprite和Value都是Cocos2d-x常用的,這裡的參數為什麼是指標?
因為值引用的參數是會拷貝一份的,這樣才不會影響原本的值,拷貝一份就會有額外的開銷。
一般類的開銷都比較大(相對於int、float等基本類型而言),所以拷貝一份不太合適。
而且我們通過都需要在函數裡改變Sprite的座標、大小等屬性,如果使用值傳遞的話,就無法修改了(修改的只是拷貝的那一份)。

當然,這個還要看具體項目的情況,我不嘮叨了,太深入不好吹水。

五、不想拷貝,又不想值被修改,怎麼辦?

拷貝開銷大,使用指標參數又很可能在函數被修改了值,怎麼辦呢?
這時候就要用const限定符了,如下代碼:

複製代碼 代碼如下:

void play(const Sprite* sp) {
}

這樣在函數內部既不會修改sp指向的值,又可以避免值傳遞的額外開銷。

六、函數內部的變數離開函數時就會被釋放

我們之前說過,只要不是new出來的變數,那麼,在離開作用範圍後,就會被自動釋放。
但是,來看看這個函數:

複製代碼 代碼如下:

int getNum() {
    int num = 10;
    return num;
}

既然變數離開作用範圍後會被釋放,那麼,num在離開getNum函數後,就會被釋放。
這時候return num的意義何在呢?getNum函數真的能成功擷取到數字10嗎?
答案是肯定的。

因為return 在返回num的時候,實際上是拷貝了一份的,返回的是拷貝的值,釋放的是原來的變數。
這就是return的秘密了。

但是,指標就不行了,看看下面的代碼:

複製代碼 代碼如下:

// 假設有這樣一個結構體
struct People {
   int age;
};
People* getNewPeople() {
    People nPeople;
    nPeople.age = 20;
    return &nPeople;
}

這個函數返回的是一個指向People結構體類型的記憶體位址。
按照return的規則,返回的時候實際上是拷貝了一份,但這個時候拷貝的只是一個指標,也就是一個記憶體位址。
這個記憶體位址仍然指向函數內部的nPeople變數。

所以即使getNewPeople函數成功返回了一個指標,但這個指標指向的記憶體位址上的值仍然是被釋放了。
也就是說,我們擷取的只是一個野指標。

七、結束

好了,這篇寫得有點糟糕,太多內容了,我只是抽取部分來吹吹水~

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.