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

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月發佈)