static[計算機高級語言]

static[計算機高級語言]
static[計算機高級語言]
更多義項 ▼ 收起列表 ▲

像在VB,C#,C,C++,Java,PHP,Objective-C中我們可以看到static作為關鍵字和函式出現,在其他的高級計算機語言如FORTRAN、ALGOL、COBOL、BASIC、LISP、SNOBOL、PL/1、Pascal、PROLOG、Ada等語言中也是有出現的,只是有著不同的作用,對於其具體作用,讀者有需要的時候是可以具體查閱的。

C++中

C++與C#的static有兩種用法:面向過程程式設計中的static和面向對象程式設計中的static。前者套用於普通變數和函式,不涉及類;後者主要說明static在類中的作用。

面向過程

靜態全局變數

在全局變數前,加上關鍵字static,該變數就被定義成為一個靜態全局變數。我們先舉一個靜態全局變數的例子,如下:

靜態全局變數有以下特點:

該變數在全局數據區分配記憶體;

未經初始化的靜態全局變數會被程式自動初始化為0(在函式體內聲明的自動變數的值是隨機的,除非它被顯式初始化,而在函式體外被聲明的自動變數也會被初始化為0);

靜態全局變數在聲明它的整個檔案都是可見的,而在檔案之外是不可見的;

靜態變數都在全局數據區分配記憶體,包括後面將要提到的靜態局部變數。對於一個完整的程式,在記憶體中的分布情況如下圖:

代碼區 //low address全局數據區堆區棧區 //high address

一般程式把新產生的動態數據存放在堆區,函式內部的自動變數存放在棧區。自動變數一般會隨著函式的退出而釋放空間,靜態數據(即使是函式內部的靜態局部變數)也存放在全局數據區。全局數據區的數據並不會因為函式的退出而釋放空間。細心的讀者可能會發現,Example 1中的代碼中將

static int n; //定義靜態全局變數

改為

int n; //定義全局變數

程式照樣正常運行。

的確,定義全局變數就可以實現變數在檔案中的共享,但定義靜態全局變數還有以下好處:

靜態全局變數不能被其它檔案所用;

其它檔案中可以定義相同名字的變數,不會發生衝突;

您可以將上述示例代碼改為如下:

編譯並運行Example 2,您就會發現上述代碼可以分別通過編譯,但運行時出現錯誤。試著將

static int n; //定義靜態全局變數

改為

int n; //定義全局變數

再次編譯運行程式,細心體會全局變數和靜態全局變數的區別。

注意:全局變數和全局靜態變數的區別

1)全局變數是不顯式用static修飾的全局變數,全局變數默認是有外部連結性的,作用域是整個工程,在一個檔案內定義的全局變數,在另一個檔案中,通過extern 全局變數名的聲明,就可以使用全局變數。

2)全局靜態變數是顯式用static修飾的全局變數,作用域是聲明此變數所在的檔案,其他的檔案即使用extern聲明也不能使用。

靜態局部變數

在局部變數前,加上關鍵字static,該變數就被定義成為一個靜態局部變數。

我們先舉一個靜態局部變數的例子,如下

通常,在函式體內定義了一個變數,每當程式運行到該語句時都會給該局部變數分配棧記憶體。但隨著程式退出函式體,系統就會收回棧記憶體,局部變數也相應失效。

但有時候我們需要在兩次調用之間對變數的值進行保存。通常的想法是定義一個全局變數來實現。但這樣一來,變數已經不再屬於函式本身了,不再僅受函式的控制,給程式的維護帶來不便。

靜態局部變數正好可以解決這個問題。靜態局部變數保存在全局數據區,而不是保存在棧中,每次的值保持到下一次調用,直到下次賦新值。

靜態局部變數有以下特點:

該變數在全局數據區分配記憶體;

靜態局部變數在程式執行到該對象的聲明處時被首次初始化,即以後的函式調用不再進行初始化;

靜態局部變數一般在聲明處初始化,如果沒有顯式初始化,會被程式自動初始化為0;

它始終駐留在全局數據區,直到程式運行結束。但其作用域為局部作用域,當定義它的函式或語句塊結束時,其作用域隨之結束;

靜態函式

在函式的返回類型前加上static關鍵字,函式即被定義為靜態函式。靜態函式與普通函式不同,它只能在聲明它的檔案當中可見,不能被其它檔案使用。

靜態函式的例子:

定義靜態函式的好處:

靜態函式不能被其它檔案所用;

其它檔案中可以定義相同名字的函式,不會發生衝突;

面向對象

(類中的static關鍵字)

靜態數據成員

在類內數據成員的聲明前加上關鍵字static,該數據成員就是類內的靜態數據成員。先舉一個靜態數據成員的例子。

可以看出,靜態數據成員有以下特點:

對於非靜態數據成員,每個類對象都有自己的拷貝。而靜態數據成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態數據成員在程式中也只有一份拷貝,由該類型的所有對象共享訪問。也就是說,靜態數據成員是該類的所有對象所共有的。對該類的多個對象來說,靜態數據成員只分配一次記憶體,供所有對象共用。所以,靜態數據成員的值對每個對象都是一樣的,它的值可以更新;

靜態數據成員存儲在全局數據區。靜態數據成員定義時要分配空間,所以不能在類聲明中定義。在Example 5中,語句int Myclass::Sum=0;是定義靜態數據成員;

靜態數據成員和普通數據成員一樣遵從public,protected,private訪問規則;

因為靜態數據成員在全局數據區分配記憶體,屬於本類的所有對象共享,所以,它不屬於特定的類對象,在沒有產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它;

靜態數據成員初始化與一般數據成員初始化不同。靜態數據成員初始化的格式為:

<數據類型><類名>::<靜態數據成員名>=<值>

類的靜態數據成員有兩種訪問形式:

<類對象名>.<靜態數據成員名> 或 <類類型名>::<靜態數據成員名>

如果靜態數據成員的訪問許可權允許的話(即public的成員),可在程式中,按上述格式來引用靜態數據成員 ;

靜態數據成員主要用在各個對象都有相同的某項屬性的時候。比如對於一個存款類,每個實例的利息都是相同的。所以,應該把利息設為存款類的靜態數據成員。這 有兩個好處,第一,不管定義多少個存款類對象,利息數據成員都共享分配在全局數據區的記憶體,所以節省存儲空間。第二,一旦利息需要改變時,只要改變一次, 則所有存款類對象的利息全改變過來了;

同全局變數相比,使用靜態數據成員有兩個優勢:

靜態數據成員沒有進入程式的全局名字空間,因此不存在與程式中其它全局名字衝突的可能性;

可以實現信息隱藏。靜態數據成員可以是private成員,而全局變數不能;

靜態成員函式

與靜態數據成員一樣,我們也可以創建一個靜態成員函式,它為類的全部服務而不是為某一個類的具體對象服務。靜態成員函式與靜態數據成員一樣,都是類的內部 實現,屬於類定義的一部分。普通的成員函式一般都隱含了一個this指針,this指針指向類的對象本身,因為普通成員函式總是具體的屬於某個類的具體對象的。通常情況下,this 是預設的。如函式fn()實際上是this->fn()。但是與普通函式相比,靜態成員函式由於不是與任何的對象相聯繫,因此它不具有this指 針。從這個意義上講,它無法訪問屬於類對象的非靜態數據成員,也無法訪問非靜態成員函式,它只能調用其餘的靜態成員函式。下面舉個靜態成員函式的例子。

關於靜態成員函式,可以總結為以下幾點:

出現在類體外的函式定義不能指定關鍵字static;

靜態成員之間可以相互訪問,包括靜態成員函式訪問靜態數據成員和訪問靜態成員函式;

非靜態成員函式可以任意地訪問靜態成員函式和靜態數據成員;

靜態成員函式不能訪問非靜態成員函式和非靜態數據成員;

由於沒有this指針的額外開銷,因此靜態成員函式與類的全局函式相比速度上會有少許的增長;

調用靜態成員函式,可以用成員訪問操作符(.)和(->;)為一個類的對象或指向類對象的指針調用靜態成員函式,也可以直接使用如下格式:

<;類名>::<;靜態成員函式名>;(<;參數表>;)

調用類的靜態成員函式。

作用

static靜態變數聲明符。在聲明它的程式塊,子程式塊或函式內部有效,值保持,在整個程式期間分配存儲器空間,編譯器默認值0。

是C++中很常用的修飾符,它被用來控制變數的存儲方式和可見性。

為什麼要引入static

函式內部定義的變數,在程式執行到它的定義處時,編譯器為它在棧上分配空間,大家知道,函式在棧上分配的空間在此函式執行結束時會釋放掉,這樣就產生了一個問題: 如果想將函式中此變數的值保存至下一次調用時,如何實現? 最容易想到的方法是定義一個全局的變數,但定義為一個全局變數有許多缺點,最明顯的缺點是破壞了此變數的訪問範圍(使得在此函式中定義的變數,不僅僅受此函式控制)。

什麼時候用static

需要一個數據對象為整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見。

內部機制

靜態數據成員要在程式一開始運行時就必須存在。因為函式在程式運行中被調用,所以靜態數據成員不能在任何函式內分配空間和初始化。

這樣,它的空間分配有三個可能的地方,一是作為類的外部接口的頭檔案,那裡有類聲明;二是類定義的內部實現,那裡有類的成員函式定義;三是應用程式的main()函式前的全局數據聲明和定義處。

靜態數據成員要實際地分配空間,故不能在類的聲明中定義(只能聲明數據成員)。類聲明只聲明一個類的“尺寸和規格”,並不進行實際的記憶體分配,所以在類聲明中寫成定義是錯誤的。它也不能在頭檔案中類聲明的外部定義,因為那會造成在多個使用該類的源檔案中,對其重複定義。

static被引入以告知編譯器,將變數存儲在程式的靜態存儲區而非棧上空間,靜態

數據成員按定義出現的先後順序依次初始化,注意靜態成員嵌套時,要保證所嵌套的成員已經初始化了。消除時的順序是初始化的反順序。

優勢

可以節省記憶體,因為它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共用。靜態數據成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態數據成員的值更新一次,保證所有對象存取更新後的相同的值,這樣可以提高時間效率。

套用格式

引用靜態數據成員時,採用如下格式:

<;類名>::<;靜態成員名>

如果靜態數據成員的訪問許可權允許的話(即public的成員),可在程式中,按上述格式來引用靜態數據成員。

注意事項

⑴類的靜態成員函式是屬於整個類而非類的對象,所以它沒有this指針,這就導致了它僅能訪問類的靜態數據和靜態成員函式。

⑵不能將靜態成員函式定義為虛函式。

⑶由於靜態成員聲明於類中,操作於其外,所以對其取地址操作,就多少有些特殊,變數地址是指向其數據類型的指針 ,函式地址類型是一個“nonmember函式指針”。

⑷由於靜態成員函式沒有this指針,所以就差不多等同於nonmember函式,結果就產生了一個意想不到的好處:成為一個callback函式,使得我們得以將C++和C-based X Window系統結合,同時也成功的套用於執行緒函式身上。

⑸static並沒有增加程式的時空開銷,相反她還縮短了子類對父類靜態成員的訪問時間,節省了子類的記憶體空間。

⑹靜態數據成員在<;定義或說明>;時前面加關鍵字static。

⑺靜態數據成員是靜態存儲的,所以必須對它進行初始化。

⑻靜態成員初始化與一般數據成員初始化不同:

初始化在類體外進行,而前面不加static,以免與一般靜態變數或對象相混淆;

初始化時不加該成員的訪問許可權控制符private,public等;

初始化時使用作用域運算符來標明它所屬類;

所以我們得出靜態數據成員初始化的格式:

<;數據類型><;類名>::<;靜態數據成員名>=<;值>

⑼為了防止父類的影響,可以在子類定義一個與父類相同的靜態變數,以禁止父類的影響。這裡有一點需要注意:我們說靜態成員為父類和子類共享,但我們有重複定義了靜態成員,這會不會引起錯誤呢?不會,我們的編譯器採用了一種絕妙的手法:name-mangling 用以生成唯一的標誌。在各通信公司的筆試面試中經常出現的考題就是static的作用及功能。

C語言中

函式分為內部函式和外部函式

當一個源程式由多個源檔案組成時,C語言根據函式能否被其它源檔案中的函式調用,將函式分為內部函式和外部函式。

內部函式(又稱靜態函式)

如果在一個源檔案中定義的函式,只能被本檔案中的函式調用,而不能被同一程式其它檔案中的函式調用,這種函式稱為內部函式。

定義一個內部函式,只需在函式類型前再加一個“static”關鍵字即可,如下所示:

static 函式類型 函式名(函式參數表){……}

關鍵字“static”,譯成中文就是“靜態的”,所以內部函式又稱靜態函式。但此處“static”的含義不是指存儲方式,而是指對函式的作用域僅局限於本檔案。

使用內部函式的好處是:不同的人編寫不同的函式時,不用擔心自己定義的函式,是否會與其它檔案中的函式同名,因為同名也沒有關係。

外部函式

外部函式的定義:在定義函式時,如果沒有加關鍵字“static”,或冠以關鍵字“extern”,表示此函式是外部函式:

[extern] 函式類型 函式名(函式參數表){……}

調用外部函式時,需要對其進行說明:

[extern] 函式類型 函式名(參數類型表)[,函式名2(參數類型表2)……];

[案例]外部函式套用。

⑴檔案mainf.c

main()

{

extern void input(…),process(…),output(…);

input(…);

process(…);

output(…);

}

⑵檔案subf1.c

……extern void input(……) /*定義外部函式*/{……}

⑶檔案subf2.c

……extern void process(……) /*定義外部 函式*/{……}

⑷檔案subf3.c

……extern void output(……) /*定義外部函式*/{……}靜態局部變數static的存儲 有時希望函式中的局部變數的值在函式調用結束後不消失而繼續保留原值,即其占用的存儲單元不釋放,在下一次再調用該函式時,該變數已有值(就是上一次函式調用結束時的值)。這時就應該指定該局部變數為“靜態局部變數”,用關鍵字static進行聲明。用靜態存儲要多占記憶體(長期占用不釋放,而不能像動態存儲那樣一個存儲單元可以先後為多個變數使用,節約記憶體),而且降低了程式的可讀性,因此若非必要,不要多用靜態局部變數。vb中語句

在過程級別中使用,用於聲明變數並分配存儲空間。在整個代碼運行期間都能保留使用 Static 語句聲明的變數的值。

static語句聲明的變數,與dim語句聲明的變數的主要區別是:前者只能在sub或function過程中使用,在退出sub或function過程後變數的值保留;後者使用在sub或function過程中時,退出sub或function過程後變數的值不保留。

語法Staticvarname[([subscripts])] [As [New]type] [,varname[([subscripts])] [As [New]type]] . . .

Static 語句的語法包含下面部分:

描述

varname 必需的。變數的名稱;遵循標準變數命名約定。

subscripts 可選的。數組變數的維數;最多可以定義 60 維的多維數組。subscripts 參數使用下面的語法:

[lower To] upper [,[lower To] upper] . . .

如果不顯式指定 lower,則數組的下界由 Option Base 語句控制。如果沒有 Option Base 語句則下界為 0。

New 可選的。用它可以隱式地創建對象的關鍵字。如果使用 New 聲明對象變數,則在第一次引用該變數時將新建該對象的實例,因此不必使用 Set 語句來對該對象引用賦值。New 關鍵字不能用來聲明任何內部數據類型的變數,也不能用來聲明從屬對象的實例。

type 可選的。變數的數據類型;可以是

Byte、Boolean、Integer、Long、Currency、Single、Double、Decimal(目前尚不支持)、Date、String(對變長的字元串)、String * length(對定長的字元串)、Object、Variant、用戶定義類型或對象類型。

所聲明的每個變數都要有一個單獨的 As type 子句。

說明

模組的代碼開始運行後,使用 Static 語句聲明的變數會一直保持其值,直至該模組復位或重新啟動。可以在非靜態的過程中使用 Static 語句顯式聲明只在該過程內可見,但具有與包含該過程定義的模組相同生命期的變數。

可以在過程中使用 Static 語句來聲明在過程調用之間仍能保持其值的變數的數據類型。例如,下面的語句聲明了一個定長的整型數組:

Static EmployeeNumber(200) As Integer

下面的語句為 worksheet 的新實例聲明了一個變數:

Static X As New Worksheet

如果在定義對象變數時沒有使用 New 關鍵字,則在使用該變數之前,必須使用 Set 語句將一個已有的對象賦給這個引用對象的變數。在被賦值之前,所聲明的這個對象變數有一個特定值 Nothing,這個值表示該變數沒有指向任何對象的實例。若在聲明中使用了 New 關鍵字,則在第一次引用對象時將新建一個該對象的實例。

如果不指定數據類型或對象類型,且在模組中沒有使用 Deftype 語句,則按預設情況,定義該變數為 Variant 類型。

注意

Static 語句與 Static 關鍵字很相似,但是針對不同的效果來使用的。如果使用 Static 關鍵字(如 Static Sub CountSales ())來聲明一個過程,則該過程中的所有局部變數的存儲空間都只分配一次,且這些變數的值在整個程式運行期間都存在。對非靜態過程而言,該過程每次被調用時都要為其變數分配存儲空間,當該過程結束時都要釋放其變數的存儲空間。Static 語句則用來在非靜態的過程中聲明特定的變數,以使其在程式運行期間能保持其值。

在初始化變數時,數值變數被初始化為 0,變長的字元串被初始化為一個零長度的字元串 (""),而定長的字元串則用 0 填充。Variant 變數被初始化為 Empty。用戶自定義類型的變數的每個元素作為各自獨立的變數進行初始化。

注意 如果在過程中使用 Static 語句,應和其它的聲明語句(如 Dim)一樣將其放在過程的開始。

作用

static的作用

在C語言中,static的字面意思很容易把我們導入歧途,其實它的作用有三條。

(1)先來介紹它的第一條也是最重要的一條:隱藏。

當我們同時編譯多個檔案時,所有未加static前綴的全局變數和函式都具有全局可見性。為理解這句話,我舉例來說明。我們要同時編譯兩個源檔案,一個是a.c,另一個是main.c。

下面是a.c的內容

char a = 'A'; // global variable

void msg() {

printf("Hello\n");

}

下面是main.c的內容

程式的運行結果是:

A Hello

你可能會問:為什麼在a.c中定義的全局變數a和函式msg能在main.c中使用?前面說過,所有未加static前綴的全局變數和函式都具有全局可見性,其它的源檔案也能訪問。此例中,a是全局變數,msg是函式,並且都沒有加static前綴,因此對於另外的源檔案main.c是可見的。

如果加了static,就會對其它源檔案隱藏。例如在a和msg的定義前加上static,main.c就看不到它們了。利用這一特性可以在不同的檔案中定義同名函式和同名變數,而不必擔心命名衝突。Static可以用作函式和變數的前綴,對於函式來講,static的作用僅限於隱藏,而對於變數,static還有下面兩個作用。

(2)static的第二個作用是保持變數內容的持久。存儲在靜態數據區的變數會在程式剛開始運行時就完成初始化,也是唯一的一次初始化。共有兩種變數存儲在靜態存儲區:全局變數和static變數,只不過和全局變數比起來,static可以控制變數的可見範圍,說到底static還是用來隱藏的。雖然這種用法不常見,但我還是舉一個例子。

程式的運行結果是:

global local static

1 10

2 9

3 8

4 7

5 6

6 5

7 4

8 3

9 2

10 1

(3)static的第三個作用是默認初始化為0。其實全局變數也具備這一屬性,因為全局變數也存儲在靜態數據區。在靜態數據區,記憶體中所有的位元組默認值都是0x00,某些時候這一特點可以減少程式設計師的工作量。比如初始化一個稀疏矩陣,我們可以一個一個地把所有元素都置0,然後把不是0的幾個元素賦值。如果定義成靜態的,就省去了一開始置0的操作。再比如要把一個字元數組當字元串來用,但又覺得每次在字元數組末尾加’\0’太麻煩。如果把字元串定義成靜態的,就省去了這個麻煩,因為那裡本來就是’\0’。不妨做個小實驗驗證一下。

程式的運行結果如下

integer: 0; string: (begin)(end)

最後對static的三條作用做一句話總結。首先static的最主要功能是隱藏,其次因為static變數存放在靜態存儲區,所以它具備持久性和默認值0。

JAVA語言中

有時你希望定義一個類成員,使它的使用完全獨立於該類的任何對象。通常情況下,類成員必須通過它的類的對象訪問,但是可以創建這樣一個成員,它能夠被它自己使用,而不必引用特定的實例。在成員的聲明前面加上關鍵字static(靜態的)就能創建這樣的成員。如果一個成員被聲明為static,它就能夠在它的類的任何對象創建之前被訪問,而不必引用任何對象。你可以將方法和變數都聲明為static。static 成員的最常見的例子是main()。因為在程式開始執行時必須調用main() ,所以它被聲明為static。

聲明為static的變數稱為靜態變數或類變數。可以直接通過類名引用靜態變數,也可以通過實例名來引用靜態變數,但最好採用前者,因為後者容易混淆靜態變數和一般變數。靜態變數是跟類相關聯的,類的所有實例共同擁有一個靜態變數。

聲明為static的方法稱為靜態方法或類方法。靜態方法可以直接調用靜態方法,訪問靜態變數,但是不能直接訪問實例變數和實例方法。靜態方法中不能使用this關鍵字,因為靜態方法不屬於任何一個實例。

舉例

如果你需要通過計算來初始化你的static變數,你可以聲明一個static塊,Static 塊僅在該類被載入時執行一次。下面的例子顯示的類有一個static方法,一些static變數,以及一個static 初始化塊:

一旦UseStatic 類被裝載,所有的static語句被運行。首先,類屬性變數開始賦值,a被設定為3,b默認初始化為 0 ,接著運行static 塊,執行(列印一條訊息),最後,b被賦值為a*4 或12。然後調用main(),main() 調用meth() ,把值42傳遞給x。3個println () 語句引用兩個static變數a和b,以及局部變數x。

注意:在一個static 方法中引用任何實例變數都是非法的。

下面是該程式的輸出:

Static block initialized.

x = 42

a = 3

b = 12

使用 static修飾符聲明屬於類型本身而不是屬於特定對象的靜態成員。 static修飾符可用於欄位、方法、屬性、運算符、事件和構造函式,但不能用於索引器、析構函式或類以外的類型。例如,下面的類聲明為 static,並且只包含 static方法。

例如:

一般來說,類中標註了static的函式與變數能在類外直接引用,比如說:

而沒有標註static的函式則必須聲明一個類的實體,有實體來引用。比如說:

C#語言中

相關詞條

相關搜尋

熱門詞條

聯絡我們