日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

static变量 static函数

發(fā)布時(shí)間:2024/9/30 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 static变量 static函数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

首先要明白c語言的存儲(chǔ)空間

轉(zhuǎn)自:http://www.52rd.com/blog/Detail_RD.Blog_imjacob_5297.html

一、c程序存儲(chǔ)空間布局

C程序一直由下列部分組成:

????? 1)正文段——CPU執(zhí)行的機(jī)器指令部分;一個(gè)程序只有一個(gè)副本;只讀,防止程序由于意外事故而修改自身指令;
????? 2)初始化數(shù)據(jù)段(數(shù)據(jù)段)——在程序中所有賦了初值的全局變量,存放在這里。
????? 3)非初始化數(shù)據(jù)段(bss段)——在程序中沒有初始化的全局變量;內(nèi)核將此段初始化為0。
????? 4)棧——增長(zhǎng)方向:
自頂向下增長(zhǎng);自動(dòng)變量以及每次函數(shù)調(diào)用時(shí)所需要保存的信息(返回地址;環(huán)境信息)。
????? 5)堆——?jiǎng)討B(tài)存儲(chǔ)分。自下而上增長(zhǎng)

|-----------|
|???????????????? |
|-----------|
|??? 棧???????? | ?高地址位
|-----------|
|??? |??????????? |?
|?? |/?????????? |
|???????????????? |
|???????????????? |
|?? /|?????????? |
|??? |??????????? |?
|-----------|
|??? 堆???????? |
|-----------|
| 未初始化? |
|-----------|
|?? 初始化? |
|-----------|
|? 正文段?? |低地址位
|-----------|


從而c語言變量有三種生命空間:程序的局部變量存在于(堆棧)中,全局變量存在于(靜態(tài)區(qū)?)中,動(dòng)態(tài)申請(qǐng)數(shù)據(jù)存在于(?堆)中。

如果在utility.h中定義了一個(gè)全局變量 如 int n = 20;

則這個(gè)頭文件只能被include一次,否則會(huì)出錯(cuò);例如在utility.cpp 中include "utility.h",然后又在main.cpp中include "utility.h";include的意思就是把頭文件的內(nèi)容全部輸出到include之處,這樣的話,就相當(dāng)于在utility.cpp中定義一個(gè)全局變量n,在main.cpp中又定義一個(gè)全局變量n,正如上文提及到的,整個(gè)工程的全局變量都放在一個(gè)地方,這樣就會(huì)出現(xiàn)重復(fù)定義。

如何在utility.h中定義一個(gè)全局變量,從而在工程的任何一個(gè)源文件,只要include一下這個(gè)頭文件,就可以直接用這個(gè)全局變量呢?有三種方法:http://hi.baidu.com/maydaygmail/blog/item/52fc1c0b6413d13e6159f3d7.html

1. (推薦)在utility.h中聲明為extern int n(意思是指這只是一個(gè)聲明,定義在外部);然后,只要在任意一個(gè).cpp?文件中再定義一次,只能定義一次,int n =10;在工程的任意地方就可以使用全局變量n,還可以修改n的值

2.?用static修飾的全局變量
??? 首先,我要告訴你static與extern是一對(duì)“水火不容”的家伙,也就是說extern和static不能同時(shí)修飾一個(gè)變量;其次,static修飾的全局變量聲明與定義同時(shí)進(jìn)行,也就是說當(dāng)你在頭文件中使用static聲明了全局變量后,它也同時(shí)被定義了;最后,static修飾全局變量的作用域只能是本身的編譯單元,也就是說它的“全局”只對(duì)本編譯單元有效,其他編譯單元?jiǎng)t看不到它,如:
??? test1.h:
??? #ifndef TEST1H
??? #define TEST1H
??? static char g_str[] = "123456";?
??? void fun1();
??? #endif

??? test1.cpp:
??? #include "test1.h"
????
??? void fun1()
??? {
??????? cout << g_str << endl;
??? }
????
??? test2.cpp
??? #include "test1.h"
????
??? void fun2()
??? {
??????? cout << g_str << endl;
??? }
????
??? 以上兩個(gè)編譯單元可以連接成功, 當(dāng)你打開test1.obj時(shí),你可以在它里面找到字符串"123456", 同時(shí)你也可以在test2.obj中找到它們,它們之所以可以連接成功而沒有報(bào)重復(fù)定義的錯(cuò)誤是因?yàn)殡m然它們有相同的內(nèi)容,但是存儲(chǔ)的物理地址并不一樣,就像是兩個(gè)不同變量賦了相同的值一樣,而這兩個(gè)變量分別作用于它們各自的編譯單元。
??? 也許你比較較真,自己偷偷的跟蹤調(diào)試上面的代碼,結(jié)果你發(fā)現(xiàn)兩個(gè)編譯單元(test1, test2)的g_str的內(nèi)存地址相同,于是你下結(jié)論static修飾的變量也可以作用于其他模塊,但是我要告訴你,那是你的編譯器在欺騙你,大多數(shù)編譯器都對(duì)代碼都有優(yōu)化功能,以達(dá)到生成的目標(biāo)程序更節(jié)省內(nèi)存,執(zhí)行效率更高,當(dāng)編譯器在連接各個(gè)編譯單元的時(shí)候,它會(huì)把相同內(nèi)容的內(nèi)存只拷貝一份,比如上面的"123456", 位于兩個(gè)編譯單元中的變量都是同樣的內(nèi)容,那么在連接的時(shí)候它在內(nèi)存中就只會(huì)存在一份了,如果你把上面的代碼改成下面的樣子,你馬上就可以拆穿編譯器的謊言:
??? test1.cpp:
??? #include "test1.h"
????
??? void fun1()
??? {
??????? g_str[0] = 'a';
??????? cout << g_str << endl;
??? }

??? test2.cpp
??? #include "test1.h"
????
??? void fun2()
??? {
??????? cout << g_str << endl;
??? }
????
??? void main()
??? {
??????? fun1(); // a23456
??????? fun2(); // 123456
??? }
????
??? 這個(gè)時(shí)候你在跟蹤代碼時(shí),就會(huì)發(fā)現(xiàn)兩個(gè)編譯單元中的g_str地址并不相同,因?yàn)槟阍谝惶幮薷牧怂?#xff0c;所以編譯器被強(qiáng)行的恢復(fù)內(nèi)存的原貌,在內(nèi)存中存在了兩份拷貝給兩個(gè)模塊中的變量使用。

??? 正是因?yàn)閟tatic有以上的特性,所以一般定義static全局變量時(shí),都把它放在原文件中而不是頭文件,這樣就不會(huì)給其他模塊造成不必要的信息污染,同樣記住這個(gè)原則吧!

3?const修飾的全局常量

??? const修飾的全局常量用途很廣,比如軟件中的錯(cuò)誤信息字符串都是用全局常量來定義的。const修飾的全局常量據(jù)有跟static相同的特性,即它們只能作用于本編譯模塊中,但是const可以與extern連用來聲明該常量可以作用于其他編譯模塊中,?如
??? extern const char g_str[];
??? 然后在原文件中別忘了定義:
??? const char g_str[] = "123456";

??? 所以當(dāng)const單獨(dú)使用時(shí)它就與static相同,而當(dāng)與extern一起合作的時(shí)候,它的特性就跟extern的一樣了!所以對(duì)const我沒有什么可以過多的描述,我只是想提醒你,const char* g_str = "123456" 與 const char g_str[] = "123465"是不同的, 前面那個(gè)const 修飾的是char * 而不是g_str,它的g_str并不是常量,它被看做是一個(gè)定義了的全局變量(可以被其他編譯單元使用), 所以如果你像讓char *g_str遵守const的全局常量的規(guī)則,最好這么定義const char* const g_str="123456".

也可以直接在頭文件中定義const char*g_str="123456"

工程任何地方都可以使用g_str,但是不能修改值


在舉一個(gè)例子:

//utiltiy.h

.......

//utility.cpp

#include "utility.h"
? ? ? ?static int m = 6;

//main.cpp

#include"utility.h"

int main()

{

cout<<m;

}

首先,雖然在main.cpp中include"utility.h",但是頭文件里沒有定義m所以,必須extern int m,表明這個(gè)m是外部的。如果在int main()之前聲明了extern int m;依然有錯(cuò),這是因?yàn)閙因?yàn)橛衧tatic現(xiàn)在,所以m只能用于utility.cpp中;

所以要改為:

//utility.cpp

#include "utility.h"
? ?int m = 6;

//main.cpp

#include"utility.h"

extern int m;

int main()

{

cout<<m;

}

這樣就對(duì)了。

如果這樣寫,對(duì)不對(duì)呢:

//utiltiy.h

?static int m = 6;

//utility.cpp

#include "utility.h"
? ? ??

//main.cpp

#include"utility.h"

int main()

{

cout<<m;

}

這樣是對(duì)的,雖然在utility.cpp和main.cpp中都有m的定義,但是此時(shí)m被static現(xiàn)在,就是說這兩個(gè)m只能在這兩個(gè)cpp文件中有效,所以不會(huì)沖突;

如果把static去掉,則就有兩個(gè)全局變量m,所以出錯(cuò)


4. 如果在.h文件中定義的全局變量,用于只讀的,還可以在頭文件中這樣定義:

? ? #define G_VAL ?5

? 或者

? enum {G_VAL = 5};


? 在其他文件中,只要include這個(gè)頭文件,就可以直接讀取G_VAL


下面講一下static函數(shù)


主要參考:http://hi.baidu.com/pope123/blog/item/344407d5512953d450da4b6c.html

在平常的?C/C++?開發(fā)中,幾乎所有的人都已經(jīng)習(xí)慣了把類和函數(shù)分離放置,一個(gè)?.h?的頭文件里放聲明,對(duì)應(yīng)的?.c?或者?.cpp?中放實(shí)現(xiàn)。從開始接觸,到熟練使用,幾乎已經(jīng)形成了下意識(shí)的流程。在?Symbian OS?下編程,則更是如此,再小的類也會(huì)分成兩個(gè)文件,幾乎沒有人想去改變。

盡管這樣的做法無可厚非,而且在不少情況下是相對(duì)合理甚至必須的,但我還是要給大家介紹一下把實(shí)現(xiàn)全部放置到頭文件中的方式,給出可供大家使用的另一個(gè)選擇。同時(shí)針對(duì)這一做法,也順便說一下其優(yōu)缺點(diǎn)以及需要注意的情況。

我是一個(gè)很喜歡簡(jiǎn)潔的人,多年以來甚至養(yǎng)成了這樣的癖好,如果一個(gè)功能是能夠用一條語句實(shí)現(xiàn)的,那就不要用兩條語句。在我看來,如果給別人提供一份可以復(fù)用的代碼的話,最優(yōu)雅的狀態(tài)莫過于僅僅提供一個(gè)頭文件就全部搞定。

之所以不太喜歡引入源文件,最重要的原因是源文件往往會(huì)帶來工程文件的變化;而且,在使用過程中也會(huì)增加一些額外的操作,例如,在一個(gè)組織良好的工程里,頭文件和源文件很有可能是位于不同的目錄,這樣就會(huì)多帶來一次文件復(fù)制操作。

2?、正文

2.1?顧慮

我遇到有不少人不使用頭文件來包含實(shí)現(xiàn),往往是出于以下幾種顧慮:

1、??????????????暴露了實(shí)現(xiàn)細(xì)節(jié)

2、??????????????頭文件被包含到不同的源文件中,會(huì)導(dǎo)致鏈接沖突

3、??????????????頭文件被包含到不同的源文件中,會(huì)導(dǎo)致有多份實(shí)現(xiàn)被編譯出來,增大可執(zhí)行體的體積

如果有顧慮?1?,那很顯然應(yīng)該在第一時(shí)間拋棄完全在頭文件中實(shí)現(xiàn)的念頭。不過我遇到的情形里,通常后兩種顧慮占據(jù)了絕對(duì)的比例。而這種顧慮,通常是由于對(duì)?C/C++?沒有足夠的了解導(dǎo)致的。

有顧慮?2?的,經(jīng)常會(huì)是一些有?C?語言開發(fā)經(jīng)驗(yàn)的程序員。他們所擔(dān)心的也往往是出現(xiàn)的全局函數(shù)的情況。例如有以下頭文件?c_function.h?(清晰起見,防衛(wèi)宏之類的代碼沒有列出):

int integer_add(const int a, const int b)

{

?????????return a + b;

}

如果在同一工程中,有?a.c?(或者是?.cpp?)和?b.c?兩個(gè)(或兩個(gè)以上)源文件包含了此頭文件,則在鏈接時(shí)期就會(huì)發(fā)生沖突,因?yàn)樵趦蓚€(gè)源文件編譯得到的目標(biāo)文件中都有一份?integer_add?的函數(shù)實(shí)現(xiàn),導(dǎo)致鏈接器不知道對(duì)于調(diào)用了此函數(shù)的調(diào)用者,應(yīng)該使用哪一個(gè)副本。(此時(shí)有兩個(gè)全局函數(shù)int integer_add(const int a, const int b)

2.2?著手

解決的辦法有兩個(gè),各自為兩個(gè)關(guān)鍵字,一個(gè)是?inline?,另一個(gè)是?static?。使用這兩個(gè)關(guān)鍵字的任意一個(gè)來修飾?integer_add?函數(shù),都會(huì)消除上述的沖突問題,然而本質(zhì)卻大不相同。

如果使用?inline?,則意味著編譯器會(huì)在調(diào)用此函數(shù)的地方把函數(shù)的目標(biāo)代碼直接插入,而不是放置一個(gè)真正的函數(shù)調(diào)用,實(shí)際作用就是這個(gè)函數(shù)事實(shí)上已經(jīng)不再存在,而是像宏一樣被就地展開了。使用?inline?的副作用,首先在于毋庸置疑地,代碼的體積變大了;其次則是,這個(gè)關(guān)鍵字嚴(yán)格算起來并不是?C?語言的關(guān)鍵字,使用它多少會(huì)帶來一些移植性方面的風(fēng)險(xiǎn),盡管主流的?C?語言編譯器都可以支持?inline?。對(duì)于?GCC?,?inline?功能關(guān)鍵字就是?inline?本身,而對(duì)于微軟的編譯器,應(yīng)該是?__inline?(注意有兩個(gè)前導(dǎo)下劃線)。

而且,根據(jù)慣例,?inline?通常都是對(duì)編譯器的某種暗示而非強(qiáng)制要求,編譯器有權(quán)力在你不知情的情況下把它實(shí)現(xiàn)為非?inline?的狀態(tài)(可能的原因有,函數(shù)太大或者復(fù)雜度過高)。這樣的后果是什么,不好意思,我沒有測(cè)試過。

如果是使用?static?,那么至少結(jié)果是可預(yù)料的。所有包含此頭文件的源文件中都會(huì)存在此函數(shù)的一份副本。雖然代碼也有一定程度的膨脹,但好就好在互相不沖突,因?yàn)?static?關(guān)鍵字保證了該函數(shù)的可見度為單個(gè)源文件之內(nèi)



static全局變量與普通的全局變量有什么區(qū)別?static局部變量和普通局部變量有什么區(qū)別?static函數(shù)與普通函數(shù)有什么區(qū)別?

全局變量(外部變量)的說明之前再冠以static?就構(gòu)成了靜態(tài)的全局變量。全局變量本身就是靜態(tài)存儲(chǔ)方式,靜態(tài)全局變量當(dāng)然也是靜態(tài)存儲(chǔ)方式。 這兩者在存儲(chǔ)方式上并無不同。這兩者的區(qū)別雖在于非靜態(tài)全局變量的作用域是整個(gè)源程序,當(dāng)一個(gè)源程序由多個(gè)源文件組成時(shí),非靜態(tài)的全局變量在各個(gè)源文件中都是有效的。而靜態(tài)全局變量則限制了其作用域, 即只在定義該變量的源文件內(nèi)有效, 在同一源程序的其它源文件中不能使用它。由于靜態(tài)全局變量的作用域局限于一個(gè)源文件內(nèi),只能為該源文件內(nèi)的函數(shù)公用,因此可以避免在其它源文件中引起錯(cuò)誤。

從以上分析可以看出, 把局部變量改變?yōu)殪o態(tài)變量后是改變了它的存儲(chǔ)方式即改變了它的生存期。把全局變量改變?yōu)殪o態(tài)變量后是改變了它的作用域,限制了它的使用范圍。

static函數(shù)與普通函數(shù)作用域不同,僅在本文件。只在當(dāng)前源文件中使用的函數(shù)應(yīng)該說明為內(nèi)部函數(shù)(static),內(nèi)部函數(shù)應(yīng)該在當(dāng)前源文件中說明和定義。對(duì)于可在當(dāng)前源文件以外使用的函數(shù),應(yīng)該在一個(gè)頭文件中說明,要使用這些函數(shù)的源文件要包含這個(gè)頭文件

static全局變量與普通的全局變量有什么區(qū)別:static全局變量只初使化一次,防止在其他文件單元中被引用;

static局部變量和普通局部變量有什么區(qū)別:static局部變量只被初始化一次,下一次依據(jù)上一次結(jié)果值;

static函數(shù)與普通函數(shù)有什么區(qū)別:static函數(shù)在內(nèi)存中只有一份,普通函數(shù)在每個(gè)被調(diào)用中維持一份拷貝


總結(jié),static限定了全局變量或者函數(shù)的作用域,使其只能在本編譯單元


static變量只被定義一次,如下題:

unsigned int sum_int( unsigned int base )
{
 unsigned int index;
 static unsigned int sum = 0; // 注意,是static類型的。?
 for (index = 1; index <= base; index++)
 {
  sum += index;
 }
 return sum;
}

  答案與分析:

  所謂的函數(shù)是可重入的(也可以說是可預(yù)測(cè)的),即:只要輸入數(shù)據(jù)相同就應(yīng)產(chǎn)生相同的輸出。
  這個(gè)函數(shù)之所以是不可預(yù)測(cè)的,就是因?yàn)楹瘮?shù)中使用了static變量,因?yàn)閟tatic變量的特征,這樣的函數(shù)被稱為:帶“內(nèi)部存儲(chǔ)器”功能的的函數(shù)。因此如果我們需要一個(gè)可重入的函數(shù),那么,我們一定要避免函數(shù)中使用static變量,這種函數(shù)中的static變量,使用原則是,能不用盡量不用。
  將上面的函數(shù)修改為可重入的函數(shù)很簡(jiǎn)單,只要將聲明sum變量中的static關(guān)鍵字去掉,變量sum即變?yōu)橐粋€(gè)auto 類型的變量,函數(shù)即變?yōu)橐粋€(gè)可重入的函數(shù)。
  當(dāng)然,有些時(shí)候,在函數(shù)中是必須要使用static變量的,比如當(dāng)某函數(shù)的返回值為指針類型時(shí),則必須是static的局部變量的地址作為返回值,若為auto類型,則返回為錯(cuò)指針。




總結(jié)

以上是生活随笔為你收集整理的static变量 static函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。