【原創】淺談指標(十二)關於static(上)

計算機知識雜談發表於2022-04-24

0.前言

這個系列基本上是一月一更到兩月一更
今天寫一篇關於static的,內含大量乾貨,做好準備

1.基礎知識的回顧

1.1.記憶體的種類

一般來說,我們之前已經講過的變數(或者說是記憶體)可以大體分為這樣幾種:

  • 全域性變數
  • 區域性變數,也稱為自動變數
  • 使用malloc分配的區域
  • 常量、字串字面量

這裡回顧一下,在C++中,使用const宣告的常量是不可改變的,也就是在編譯期就確定下來了。因此,即使使用指標更改也不會實際修改到它的值。對於全域性變數,const出的值和字串字面量(即使用""括起來的字串),存在常量區,強制改變會使得程式異常退出。

1.2.作用域和生命週期

對於全域性變數,它由始至終都是存在的,作用域是全部。
區域性變數的作用域和宣告週期僅存在一個函式中,當函式返回,它就會從棧中銷燬。
使用malloc分配的記憶體區域,它的生命週期一直到呼叫free為止。
對於字串字面量和常量,它的作用域和宣告週期與全域性變數和區域性變數類似。

2.static的相關用法

2.1.靜態變數的定義

我們把使用static修飾的變數和全域性變數統稱為靜態變數。
靜態變數,顧名思義,就是可以貫穿整個程式執行的時間內的變數。

2.2.static的地址

我們來寫一段程式碼,進行一個實驗:

#include<iostream>
#include<windows.h>
using namespace std;
int a;//全域性變數
static int b;//全域性static變數
void f(void){
    static int c;//定義在函式內的static變數
    printf("c..%p\n",&c);
}
int main(){
    printf("a..%p\n",&a);
    printf("b..%p\n",&b);
    f();
    return 0;
}

(注:今天我換了一臺電腦進行編輯,使用的是codeblocks來編輯,編譯器我設定的是VC)
輸出的結果如下

可以看到,static修飾的變數,與全域性變數的地址是接近的,可以證明它是在全域性儲存區。

2.3.函式體內的static

還是以例子來說明,這樣比較好理解。假如我們寫一個將數字轉為字串的函式:

#include<iostream>
#include<windows.h>
using namespace std;
char *toint(int x){
    char s[1000];
    sprintf(s,"%d",x);
    return s;
}
int main(){
    char s[1000],t[1000];
    strcpy(s,toint(8));
    strcpy(t,toint(10));
    printf("%s\n%s\n",s,t);
    return 0;
}

使用sprintf函式,進行字串間的轉換。
這段程式碼,乍一看似乎沒有問題,而且在我的環境還可以正常執行:(部分環境會Segmentation Fault,就更加能說明這個問題)

但是我們仔細看看,畫面下方報出了一行警告:

(看我選中的一條,上面一條似乎是環境沒有配置到位,先不管了)

這是因為,其中的s陣列是區域性變數,或者說是自動變數,儲存在棧中,在函式返回之後,這個地址就不能再使用了,因為這個陣列已經銷燬了,s地址所在的地方是“無人區”,訪問時就有可能訪問到不該訪問的資料,進而出錯。

對於這一類的問題,解決方法有使用malloc和new來分配記憶體,這樣可以在free之前多次使用:

char *toint(int x){
    char *s=new char [1000];
    sprintf(s,"%d",x);
    return s;
}

這一次沒有報錯。

事實上,還可以使用靜態變數來解決(不過靜態變數主要的用途不在這裡),這樣這個記憶體就不會在返回的時候被釋放。

char *toint(int x){
    static char s[1000];
    sprintf(s,"%d",x);
    return s;
}

同樣沒有報錯。

3.static的更多特性與用途

3.1.在函式退出後,static變數的值保持不變

#include<iostream>
#include<windows.h>
using namespace std;
void f(){
    static int Count;
    printf("%d\n",Count);
    Count++;
}
int main(){
    for(int i=0;i<10;i++){
        f();
    }
}

由於static的變數一直在同一個儲存區,因此可以發現,退出函式時,static變數的值保持不變,輸出結果為:

3.2.多檔案中的使用

static的變數,只能在當前的檔案內進行訪問。

//a.cpp
static int x;

int main(){
  x=100;
  cout<<x<<endl;
  f();
}

//b.cpp
extern int x;
void f(){
  cout<<x<<endl;
}

在b.cpp中,無法訪問a.cpp中的x變數,因為x是使用static修飾的(即使使用了extern進行宣告)

包括函式也可以使用static進行修飾:

static int f();

關於更多的內容,敬請期待:
【原創】淺談指標(十三)關於static(下)

(預計5月釋出)

相關文章