<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                【54.1 本節閱讀前的名詞約定。】 變量可以粗略的分成兩類,一類是全局變量,一類是局部變量。如果更深一步精細劃分,全局變量還可以分成“普通全局變量”和“靜態全局變量”,局部變量也可以分成“普通局部變量”和“靜態局部變量”,也就是說,若精細劃分,可以分成四類。其中“靜態全局變量”和“靜態局部變量”多了一個前綴“靜態”,這個前綴“靜態”是因為在普通的變量前面多加了一個修飾關鍵詞“static”,這部分的內容后續章節會講到。本節重點為了讓大家理解內存模型的“棧”,暫時不考慮“靜態變量”的情況,人為約定,本節所涉及的“全局變量”僅僅默認為“普通全局變量”,“局部變量”僅僅默認為“普通局部變量”。 【54.2 如何判定全局變量和局部變量?】 全局變量就是在函數外面定義的變量,局部變量就是在函數內部定義的變量,這是最直觀的判定方法。下面的例子能很清晰地說明全局變量和局部變量的判定方法: unsigned char a; //在函數外面定義的,所以是全局變量。 void main() //主函數 { unsigned char b; //在函數內部定義的,所以是局部變量。 b=a; while(1) { } } 【54.3 全局變量和局部變量的內存模型。】 單片機內存包括ROM和RAM兩部分,ROM存儲的是單片機程序中的指令和一些不可更改的常量數據,而RAM存放的是可以被更改的變量數據,也就是說,全局變量和局部變量都是存放在RAM,但是,雖然都是存放在RAM,全局變量和局部變量之間的內存模型還是有明顯的區別的,因此,分了兩個不同的RAM區,全局變量占用的RAM區稱為“全局數據區”,局部變量占用的RAM區稱為“棧”,因為我后面會用賓館來比喻“棧”,為了方便記憶,大家可以把“棧”想象成 “客棧”來記憶。它們的內存模型到底有什么本質的區別呢?“全局數據區”就像你自己家的房間,是唯一的,一個房間的地址只能你一個人住(假設你還沒結婚的時候),而且是永久的,所以說每個全局變量都有唯一對應的RAM地址,不可能重復的。而“棧”就像賓館客棧,一年下來每天晚上住的人不一樣,每個人在里面居住的時間是有期限的,不是長久的,一個房間的地址一年下來每天可能住進不同的人,不是唯一的。“全局數據區”的全局變量擁有永久產權,“棧”區的局部變量只能臨時居住在賓館客棧,地址不是唯一的,有期限的。全局變量像私人區,局部變量像公共區。“棧”的這片公共區,是給程序里所有函數內部的局部變量共用的,函數被調用的時候,該函數內部的每個局部變量就會被分配對應到“棧”的某個RAM地址,函數調用結束后,該局部變量就失效,因此它對應的“棧”的RAM空間就被收回以便給下一個被調用的函數的局部變量占用。請看下面這個例子,我借用“賓館客棧”來比喻局部變量所在的“棧”。 void HanShu(void); //子函數的聲明 void HanShu(void) //子函數的定義 { unsigned char a; //局部變量 a=1; } void main() //主函數 { HanShu() ; //子函數的調用 } 分析:上述例子,單片機從主函數main往下執行,首先遇到HanShu子函數的調用,所以就跳到HanShu函數的定義那里開始執行,此時的局部變量a開始被分配在RAM的“棧區”的某個地址,相當于你入住賓館被分配到某個房間。單片機執行完子函數HanShu后,局部變量a在RAM的“棧區”所分配的地址被收回,局部變量a消失,被收回的RAM地址可能會被系統重新分配給其它被調用的函數的局部變量,此時相當于你離開賓館,從此你跟那個賓館的房間沒有啥關系,你原來在賓館入住的那個房間會被賓館老板重新分配給其他的客人入住。全局變量的作用域是永久性不受范圍限制的,而局部變量的作用域就是它所在函數的內部范圍。全局變量的“全局數據區”是永久的私人房子(這里的“永久”僅僅是舉一個例子,別拿“70年產權”來抬杠),局部變量的“棧”是臨時居住的“客棧”。重要的事情說兩遍,再次總結如下: (1)每定義一個新的全局變量,就意味著多開銷一個新的RAM內存。而每定義一個局部變量,只要在函數內部所定義的局部變量總數不超過單片機的“棧”區,此時的局部變量不開銷新的RAM內存,因為局部變量是臨時借用“棧”區的,使用后就還給“棧”,“棧”是公共區,可以重復利用,可以服務若干個不同的函數內部的局部變量。 (2)單片機每次進入執行函數時,局部變量都會被初始化改變,而全局變量則不會被初始化,全局變量是一直保存之前最后一次更改的值。 【54.4 三個常見疑問。】 第一個疑問: 問:“全局數據區”和“棧區“是誰在幕后分配的,怎么分配的? 答:是C編譯器自動分配的,至于怎么分配,誰分配多一點,誰分配少一點,C編譯器會有一個默認的比例分配,我們一般都不用管。 第二個疑問: 問:“棧”區是臨時借用的,子函數被調用的時候,它內部的局部變量才會“臨時”被分配到“棧”區的某個地址,那么問題來了,誰在幕后主持“棧區”這些分配的工作,難道也是C編譯器?C編譯器不是在編譯程序的時候一次性就做完了編譯工作然后就退出歷史舞臺了嗎?難道我們程序已經在單片機內部運轉的時候,編譯器此時還在幕后指手畫腳的起作用? 答:單片機已經上電開始運行程序的時候,編譯器是不可能起作用的。所以,真相只有一個,“棧區”分配給函數內部局部變量的工作,確實是C編譯器做的,唯一需要注意的地方是,它不是“現炒現賣”,而是在單片機上電前,C編譯器就把所有函數內部的局部變量的分配工作就規劃好了,都指定了如果某個函數一旦被調用,該函數內部的哪個局部變量應該分到“棧區”的哪個地址,C編譯器都是事先把這些“后事”都交代完畢了才“結束自己的生命”,后面,等單片機上電開始工作的時候,雖然C編譯器此時“不在”了,但是單片機都是嚴格按照C編譯器交代的“遺囑”開始工作和分配“棧區”的。因此,“棧區”的“臨時分配”非真正嚴格意義上的“臨時分配”。 第三個疑問: 問:函數內部所定義的局部變量總數不超過單片機的“棧”區的RAM數量,那,萬一超過了“棧”區的RAM數量,后果嚴重嗎? 答:后果特別嚴重。這種情況,專業術語叫“爆棧”。程序會出現異常,而且是莫名其妙的異常。為了避免這種情況,一般在編寫程序的時候,函數內部都不能定義大數組的局部變量,局部變量的數量不能定義太多太大,尤其要避免剛才所說的定義開辟大數組局部變量這種情況。大數組的定義應該定義成全局變量,或者定義成“靜態的局部變量”(“靜態”這部分相關的內容后面章節會講到)。有一些C編譯器,遇到“爆棧”的情況,會好心跟你提醒讓你編譯不過去,但是也有一些C編譯器可能就不會給你提醒,所以大家以后做項目寫函數的時候,要對“爆棧”心存敬畏。 【54.5 全局變量和局部變量的優先級。】 剛才說到,全局變量的作用域是永久性并且不受范圍限制的,而局部變量的作用域就是它所在函數的內部范圍,那么問題來,假如局部變量和全局變量的名字重名了,此時函數內部執行的變量到底是局部變量還是全局變量?這個問題就涉及到優先級。注意,當面對同名的局部變量和全局變量時,函數內部執行的變量是局部變量,也就是局部變量在函數內部要比全局變量的優先級高。為了深刻理解“全局變量和局部變量的優先級”,強烈建議大家必須仔細看完下面列舉的三個練習例子。 【54.6 例程練習和分析。】 請看下面第一個例子: /\*---C語言學習區域的開始。-----------------------------------------------\*/ unsigned char a=5; //此處第1個a是全局變量。 void main() //主函數 { unsigned char a=2; //此處第2個a是局部變量。跟上面全局變量的第1個a重名了! View(a); //把a發送到電腦端的串口助手軟件上觀察。 while(1) { } } /\*---C語言學習區域的結束。-----------------------------------------------\*/ 分析: 上述例子,有2個變量重名了!其中一個是全局變量,另外一個是局部變量。此時輸出顯示的結果是5還是2?正確的答案是2。因為在函數內部,函數內部的局部變量比全局變量的優先級更加高。此時View(a)是第2個局部變量的a,而不是第1個全局變量的a。雖然這里的兩個a重名了,但是它們的內存模型不一樣,第1個全局變量的a是分配在“全局數據區”是具有唯一的地址的,而第2個局部變量的a是被分配在臨時的“棧”區的,寄生在main函數內部。 再看下面第二個例子: /\*---C語言學習區域的開始。-----------------------------------------------\*/ void HanShu(void); //函數聲明 unsigned char a=5; //此處第1個a是全局變量。 void HanShu(void) //函數定義 { unsigned char a=3; //此處第2個a是局部變量。 } void main() //主函數 { unsigned char a=2; //此處第3個a也是局部變量。 HanShu(); //子函數被調用 View(a); //把a發送到電腦端的串口助手軟件上觀察。 while(1) { } } /\*---C語言學習區域的結束。-----------------------------------------------\*/ 分析: 上述例子,有3個變量重名了!其中一個是全局變量,另外兩個是局部變量。此時輸出顯示的結果是5還是3還是2?正確的答案是2。因為,HanShu這個子函數是被調用結束之后,才執行View(a)的,就意味HanShu函數內部的局部變量(第2個局部變量a)是在執行View(a)語句的時候就消亡不存在了,所以此時View(a)的a是第3個局部變量的a(在main函數內部定義的局部變量的a)。 再看下面第三個例子: /\*---C語言學習區域的開始。-----------------------------------------------\*/ void HanShu(void); //函數聲明 unsigned char a=5; //此處第1個a是全局變量。 void HanShu(void) //函數定義 { unsigned char a=3; //此處第2個a是局部變量。 } void main() //主函數 { HanShu(); //子函數被調用 View(a); //把a發送到電腦端的串口助手軟件上觀察。 while(1) { } } /\*---C語言學習區域的結束。-----------------------------------------------\*/ 分析: 上述例子,有2個變量重名了!其中一個是全局變量,另外一個是局部變量。此時輸出顯示的結果是5還是3?正確的答案是5。因為,HanShu這個子函數是被調用結束之后,才執行View(a)的,就意味HanShu函數內部的局部變量(第2個局部變量)是在執行View(a)語句的時候就消亡不存在了,同時,因為此時main函數內部也沒有定義a的局部變量,所以此時View(a)的a是必然只能是第1個全局變量的a(在main函數外面定義的全局變量的a)。 【54.7 如何在單片機上練習本章節C語言程序?】 直接復制前面章節中第十一節的模板程序,練習代碼時只需要更改“C語言學習區域”的代碼就可以了,其它部分的代碼不要動。編譯后,把程序下載進帶串口的51學習板,通過電腦端的串口助手軟件就可以觀察到不同的變量數值,詳細方法請看第十一節內容。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看