C語言中的static 詳細分析

2022-03-27 11:21:55 字數 4291 閱讀 2510

c語言**是以檔案為單位來組織的,在乙個源程式的所有原始檔中,乙個外部變數(注意不是區域性變數)或者函式只能在乙個源程式中定義一次,如果有重複定義的話編譯器就會報錯。伴隨著不同原始檔變數和函式之間的相互引用以及相互獨立的關係,產生了extern和static關鍵字。

下面,詳細分析一下static關鍵字在編寫程式時有的三大類用法:

一,static全域性變數

我們知道,乙個程序在記憶體中的布局如圖1所示:

其中.text段儲存程序所執行的程式二進位制檔案,.data段儲存程序所有的已初始化的全域性變數,.bss段儲存程序未初始化的全域性變數(其他段中還有很多亂七八糟的段,暫且不表)。在程序的整個生命週期中,.data段和.bss段內的資料時跟整個程序同生共死的,也就是在程序結束之後這些資料才會壽終就寢。

當乙個程序的全域性變數被宣告為static之後,它的中文名叫靜態全域性變數。靜態全域性變數和其他的全域性變數的儲存地點並沒有區別,都是在.data段(已初始化)或者.bss段(未初始化)內,但是它只在定義它的原始檔內有效,其他原始檔無法訪問它。所以,普通全域性變數穿上static外衣後,它就變成了新娘,已心有所屬,只能被定義它的原始檔(新郎)中的變數或函式訪問。

以下是一些示例程式

file1.h如下:

#include 

void

printstr();  

我們在file1.c中定義乙個靜態全域性變數hello, 供file1.c中的函式printstr訪問.

#include "file1.h"

static

char

* hello = 

"hello cobing!"

;  void

printstr()  

file2.c是我們的主程式所在檔案,file2.c中如果引用hello會編譯出錯

#include "file1.h"

intmain()  

報錯如下:

[liujx@server235 static]$ gcc -wall file2.c file1.c -o file2

file2.c: in function 『main』:

file2.c:6: 錯誤:『hello』 未宣告 (在此函式內第一次使用)

file2.c:6: 錯誤:(即使在乙個函式內多次出現,每個未宣告的識別符號在其

file2.c:6: 錯誤:所在的函式內只報告一次。)

如果我們將file2.c改為下面的形式:

#include "file1.h"

intmain()  

則會順利編譯連線。

執行程式後的結果如下:

[liujx@server235 static]$ gcc -wall file2.c file1.c -o file2

[liujx@server235 static]$ ./file2

hello cobing!

上面的例子中,file1.c中的hello就是乙個靜態全域性變數,它可以被同一檔案中的printstr呼叫,但是不能被不同原始檔中的file2.c呼叫。

二,static區域性變數

普通的區域性變數在棧空間上分配,這個區域性變數所在的函式被多次呼叫時,每次呼叫這個區域性變數在棧上的位置都不一定相同。區域性變數也可以在堆上動態分配,但是記得使用完這個堆空間後要釋放之。

static區域性變數中文名叫靜態區域性變數。它與普通的區域性變數比起來有如下幾個區別:

1)位置:靜態區域性變數被編譯器放在全域性儲存區.data(注意:不在.bss段內,原因見3)),所以它雖然是區域性的,但是在程式的整個生命週期中存在。

2)訪問許可權:靜態區域性變數只能被其作用域內的變數或函式訪問。也就是說雖然它會在程式的整個生命週期中存在,由於它是static的,它不能被其他的函式和原始檔訪問。

3)值:靜態區域性變數如果沒有被使用者初始化,則會被編譯器自動賦值為0,以後每次呼叫靜態區域性變數的時候都用上次呼叫後的值。這個比較好理解,每次函式呼叫靜態區域性變數的時候都修改它然後離開,下次讀的時候從全域性儲存區讀出的靜態區域性變數就是上次修改後的值。

以下是一些示例程式:

file1.h的內容和上例中的相同,file1.c的內容如下:

#include "file1.h"

void

printstr()  

為了便於比較,我定義了兩個變數:普通區域性變數normal和靜態區域性變數stat,它們都被賦予初值0;

file2.c中呼叫file1.h:

#include "file1.h"

intmain()  

這個呼叫會報錯,因為file2.c中引用了file1.c中的靜態區域性變數stat,如下:

[liujx@server235 static]$ gcc -wall file2.c file1.c -o file2

file2.c: in function 『main』:

file2.c:9: 錯誤:『stat』 未宣告 (在此函式內第一次使用)

file2.c:9: 錯誤:(即使在乙個函式內多次出現,每個未宣告的識別符號在其

file2.c:9: 錯誤:所在的函式內只報告一次。)

編譯器說stat未宣告,這是因為它看不到file1.c中的stat,下面注掉這一行:

#include "file1.h"

intmain()  

[liujx@server235 static]$ gcc -wall file2.c file1.c -o file2

[liujx@server235 static]$ ./file2

normal = 0 ---- stat = 0

normal = 0 ---- stat = 1

normal = 0 ---- stat = 2

normal = 0 ---- stat = 3

執行如上所示。可以看出,函式每次被呼叫,普通區域性變數都是重新分配,而靜態區域性變數保持上次呼叫的值不變。

需要注意的是由於static區域性變數的這種特性,使得含靜態區域性變數的函式變得不可重入,即每次呼叫可能會產生不同的結果。這在多執行緒程式設計時可能會成為一種隱患。需要多加注意。

三,static函式

相信大家還記得c++物件導向程式設計中的private函式,私有函式只有該類的成員變數或成員函式可以訪問。在c語言中,也有「private函式」,它就是接下來要說的static函式,完成物件導向程式設計中private函式的功能。

當你的程式中有很多個原始檔的時候,你肯定會讓某個原始檔只提供一些外界需要的介面,其他的函式可能是為了實現這些介面而編寫,這些其他的函式你可能並不希望被外界(非本原始檔)所看到,這時候就可以用static修飾這些「其他的函式」。

所以static函式的作用域是本原始檔,把它想象為物件導向中的private函式就可以了。

下面是一些示例:

file1.h如下:

#include 

static

intcalled();  

void

printstr();  

file1.c如下:

#include "file1.h"

static

intcalled()  

void

printstr()  

file2.c中呼叫file1.h中宣告的兩個函式,此處我們故意呼叫called():

#include "file1.h"

intmain()  

編譯時會報錯:

[liujx@server235 static]$ gcc -wall file2.c file1.c -o file2

file1.h:3: 警告:『called』 使用過但從未定義

/tmp/ccylubzu.o: in function `main':

file2.c:(.text+0x12): undefined reference to `called'

collect2: ld 返回 1

因為引用了file1.h中的static函式,所以file2.c中提示找不到這個函式:undefined reference to 'called'

下面修改file2.c:

#include "file1.h"

intmain()  

編譯執行:

[liujx@server235 static]$ gcc -wall file2.c file1.c -o file2

[liujx@server235 static]$ ./file2

returnval=6

static函式可以很好地解決不同原檔案中函式同名的問題,因為乙個原始檔對於其他原始檔中的static函式是不可見的。

遞迴演算法詳細分析 C語言

c通過執行時堆疊支援遞迴函式的實現。遞迴函式就是直接或間接呼叫自身的函式。許多教科書都把計算機階乘和菲波那契數列用來說明遞迴,非常不幸我們可愛的著名的老潭老師的 c語言程式設計 一書中就是從階乘的計算開始的函式遞迴。導致讀過這本經書的同學們,看到階乘計算第乙個想法就是遞迴。但是在階乘的計算裡,遞迴並...

C 陣列 詳細分析

c 陣列 詳細分析 摘自 1 陣列下標 1 在定義時必須明確。只能用正整數或const常量,靜態或全域性變數不可以 例如 const int length 5 int array length 特 在new時可以用變數做下標。例如 int array new int variable 2 在初始化時...

C 陣列 詳細分析

1 陣列下標 1 在定義時必須明確。只能用正整數或const常量,靜態或全域性變數不可以 例如 const int length 5 int array length 特 在new時可以用變數做下標。例如 int array new int variable 2 在初始化時 一維陣列,可略 例如 i...