1. 程式人生 > >C++的基礎知識(十四)--變數的作用域與生命週期

C++的基礎知識(十四)--變數的作用域與生命週期

生命週期

  • 變數的生命週期指可以使用變數儲存位置的時間範圍
  • 變數的儲存位置在程式執行期間不停地被建立又不停被回收
  • 根據生命週期也可以把變數分為全域性,區域性和塊
    全域性變數的生命週期是整個程式執行期間
    區域性變數的生命週期是函式呼叫期間
    塊變數的生命週期是塊語句執行期間
  • 函式的形參可以看成是區域性變數,他們的生命週期也是函式執行期間

    同一個函式同一個區域性變數呼叫的儲存位置是不一樣的

  #include <stdio.h>

  void f1(void)
  {
      int num1;
      num1++;
      printf("num1是%d\n"
, num1); } void f2(void) { int num = 10; int num1 = 11; int num2 = num + num1; f1(); } int main(int argc, char **argv) { f1(); f2(); return 0; }

這裡f1()函式呼叫了兩次,然後兩次的結果是沒有任何關係的。
遞迴函式的arr陣列定義為全域性變數,則整個函式都使用這一個變數,所以函式執行時間快。
重名變數

#include <stdio.h>
int num; int main(int argc, char **argv) { printf("%d\n", num); //num = 0 int num = 1; printf("%d\n", num); //num = 1 { printf("%d\n", num); //num = 1 int num = 2; printf("%d\n", num); //num = 2 } printf("%d\n", num); //num = 1
return 0; }

同名變數中優先使用塊變數,其次為區域性變數,最後為全域性變數。

宣告變數時的關鍵字
1、auto宣告自動變數,一般不用
2、static關鍵字宣告靜態變數,靜態變數的生命週期和作用域不匹配,一個大一個小。靜態全域性變數的生命週期不變,作用域是檔案內部的所有語句,別的檔案不能使用。靜態區域性變數作用域不變,生命週期為整個程式執行期間,

#include <stdio.h>

  void f1(void)
  {
      static int num1;
      num1++;
      printf("num1是%d\n", num1);
  }

  void f2(void)
  {
      int num = 10;
      int num1 = 11;
      int num2 = num + num1;
      f1();
  }

  int main(int argc, char **argv)
  {
      f1();
      f2();
      return 0;

執行結果為1,2。在加了static之後,兩次呼叫f1的結果就有關係了,因為靜態區域性變數初始化預設為0。因為不管呼叫多少次,靜態區域性變數都是同一個儲存位置,他的生命週期為整個程式執行期間。而且靜態區域性變數的初始化只是程式開始的時候執行一次。剛才的arr陣列也可以用靜態變數解決。

3、const關鍵字用來宣告不可賦值的變數,只有在初始化時能夠賦值。但是能夠被改變。

4、volatile關鍵字也可以用來宣告變數,其內容隨時可能變化。好比你的工資卡有5000塊錢交給您夫人了,但是第二天你去看的時候只有3000了,您夫人拿2000買了雙鞋。。。。。。

為什麼我們的儲存位置不停地被收走,又不停的被分配?
程式執行時,檔案內容讀到記憶體中,被分成好幾個段,每個段落為不同的儲存資料格式,以及不同的資料使用方式。
程式執行時,檔案內容讀到記憶體中,被分成好幾個段,每個段落為不同的儲存資料格式,以及不同的資料使用方式。

程式碼段:用於儲存語句轉化成的數字,這個段落在執行時是不可以被修改。
全域性段:包含所有全域性變數和靜態變數的儲存位置,段落大小不會隨著程式執行而改變
棧:包含所有區域性變數,塊變數,形式引數和返回值的儲存位置,段落大小隨著程式執行不斷改變。
int main(int argc, char **argv)
{  
     f1();
     f2();
     return 0;
}

這裡寫圖片描述
main函式的位置是固定不變的,而其他的函式都是向下移動的,棧為後進先出,最後執行的程式最先被回收,像垃圾桶一樣。最後扔進去的垃圾最先倒出來,每次函式呼叫棧中佔有專門的儲存區域,當一個函式呼叫結束後它所佔有的區域被計算機回收。

堆:包含所有動態分配的儲存位置,堆中包含的儲存位置可以看成是無限的,堆中儲存位置的生命週期由程式猿管理,需要使用語句分配和回收

這裡寫圖片描述

遞迴函式
什麼是遞迴函式?

遞迴函式內部包含一條或多條函式呼叫語句,被呼叫函式就是它自己。

 #include <stdio.h>

  void print(int num)
  {
      if (num == 1)
      {
          printf("1\n"); 
          return ;
      }
      printf("%d ", num);
      print(num - 1);
  }    

  int main(int argc, char **argv)
  {
      print(5); 
      return 0;
  }
 #include <stdio.h>
  int sum(int num)
  {

      if (num == 1)
      {
          return ;
      }

      return num + sum(num - 1);

  }


  int main(int argc, char **argv)
  {
      int num = 0;
      printf("請輸入一個整數:");
      scanf("%d", &num);
      sum(num);
      printf("%d到1的和為%d\n", num, sum(num));
      return 0;
  }

這是一個求和的遞迴函式。
遞迴函式如何執行?

下面用上面的求和函式聊一聊
這裡寫圖片描述
這是一個求3到1的和的過程,主函式裡面執行sum(3),然後sum(3)又要執行sum(2),sum(2)再執行sum(1),sum(1)執行自己,所以在那段相同的時間中,sum(1)永遠在被執行,這段時間所有的呼叫都在進行。所以任何時候在真正工作的只有sum(1)。
下面再給出一個斐波那契數列的例子

/*1 2 3 5 8 13 21 34..........  
  1 2 3 4 5 6   7  8
  給一個編號,說出斐波那契對應數列的值
*/
  #include <stdio.h>
  //int arr[50] = {};
  int Fibonacci(int num)
  {
     int arr[50] = {};
     if (num <= 2)
     {
         return 1;
     }
     if (!arr[num - 2])
     {
        arr[num - 2] = Fibonacci(num - 2);
     }
     if (!arr[num - 1])
     {
        arr[num - 1] = Fibonacci(num - 1);
     }
     return Fibonacci(num - 2) + Fibonacci(num - 1);
  }

 int main(int argc, char **argv)
 {
     int num = 0;
     printf("請輸入編號:");
     scanf("%d", &num);
     printf("編號為%d的數字是%d\n", Fibonacci(num));
     return 0;
 }

執行這段程式,當輸入數字小的時候不會有什麼感覺,但是我們輸入大數字的時候,比如20,那程式執行的結果會很慢才出現。 但是我們將arr陣列定義在函式外面,那結果就不一樣,輸入20的時候,結果很快就能出來。這是因為我們的數列在計算的時候每執行一次都要計算前面一個數的值,所以根據剛才的遞迴道理,每個呼叫都要呼叫前面的數值,導致執行時間很長,我們雖然用了陣列去儲存每個值,但是他只是在我們的函式裡面,在整個函式來說是不起作用的,他還是要重新計算每次的值

這裡就關係到一個區域性變數,全域性變數和塊變數的問題了

全域性變數的作用域包含程式中所有語句,未初始化的全域性變數自動初始化為0。
區域性變數宣告在函式內部,作用域包括函式內部所有語句。
塊變數是宣告在函式內部的大括號中,塊變數的作用域包含語句塊中的所有語句

#include <stdio.h>
int num = 0;    //全域性變數
int main(int argc, char **argv)
{ 
    int num1 = 0;    //區域性變數
    printf("num1是%d\n", num1);
    {
        int num2 = 0;  //塊變數
        printf("%d\n", num1);     //合法,因為在宣告在函式內部
    }
}
printf("num2是%d\n", num2);    //錯,在塊裡面宣告,作用域只在塊裡面