全面了解C語言?static?關(guān)鍵字
一,前言
大家好,歡迎來到C語言深度解析專欄—關(guān)鍵字詳解第二篇,在本篇中我們將會對static關(guān)鍵字進行詳細介紹,其中要求我們掌握我上一篇中所講到的全局變量、局部變量、作用域以及生命周期的相關(guān)概念,如果對這幾個概念比較模糊的同學(xué)可以先移步我上一篇博客,下面是博客鏈接。
C語言關(guān)鍵字詳解
二、認識多文件
為了理解static修飾函數(shù)的作用,我們需要了解多文件的相關(guān)內(nèi)容
1、多文件的創(chuàng)建
這里我先介紹一下頭文件的創(chuàng)建:頭文件的創(chuàng)建與.c文件的創(chuàng)建十分相似,僅僅是在選擇的時候把c++文件改成.h而已
.h:我們稱之為頭文件,一般包含函數(shù)聲明,變量聲明,宏定義,頭文件等內(nèi)容(header)
.c: 我們稱之為源文件,一般包含函數(shù)實現(xiàn),變量定義等 (.c:c語言)
多文件就是在一個.h文件下,包含多個.c文件,比如main.c test1.c test2.c teset3.c … …
2、為什么要有多文件
在一個公司的大型項目中,預(yù)期產(chǎn)品所要實現(xiàn)的功能往往是十分復(fù)雜的,所以一般都會將功能進行模塊化處理,從而便于我們進行代碼的復(fù)用、代碼的修改與維護以及多人協(xié)作,自然我們一個程序中就需要多個.c文件
3、為什么要有頭文件
單純的使用源文件,組織項目結(jié)構(gòu)的時候,項目越大越復(fù)雜維護成本會變得越來越高!
所以我們在組織項目結(jié)構(gòu)的時候會使用頭文件來減少大型項目的維護成本問題。
補充:頭文件中 #pragma once 的含義
大家在創(chuàng)建一個.h 頭文件的時候會發(fā)現(xiàn)編譯器在頭文件的開頭會自動加上 #pragma once
相信有許多小伙伴在曾今或者現(xiàn)在都對這東西有著深深的疑惑,其實它是用來防止頭文件被反復(fù)包含的,舉個栗子
如上所示:我在test.h中包含了頭文件<stdio.h>,但是在main.c中我又同時包含了test.h 和 stdio.h ,這就造成了stdio.h被包含了兩次,使得程序在編譯的時候?qū)tdio.h 里面的內(nèi)容拷貝了兩份,造成代碼冗余,而#pragma once 會檢查該頭文件是否已經(jīng)被包含,如若是就不在進行拷貝。
防止頭文件反復(fù)包含的另一種方法(涉及預(yù)處理內(nèi)容,暫時不講,同學(xué)們當作了解即可)
4、多文件在代碼中的具體體現(xiàn)
在上圖中我們在test.c 文件中中定義了一個全局變量和一個函數(shù),然后在test.h文件中對其進行聲明,最后在main.c文件中對全局變量和函數(shù)進行打印和調(diào)用,我們可以發(fā)現(xiàn),這種做法是可行的,也就是說:全局變量和函數(shù)可以跨文件訪問的(這個結(jié)論在解釋下文static作用時會被用到)
三、最名不符實的關(guān)鍵字 - static
static 整體闡述
上圖是MSDN對static的解釋,翻譯過來就是:修改變量時,static關(guān)鍵字指定該變量具有靜態(tài)持續(xù)時間(在程序開始時分配,在程序結(jié)束時釋放),并將其初始化為0,除非指定了其他值。在文件范圍中修改變量或函數(shù)時,static關(guān)鍵字指定該變量或函數(shù)具有內(nèi)部鏈接(其名稱在聲明它的文件外部不可見)。這段話讀起來沒什么具體的概念,接下來我從static 作用的三個對象來帶大家具體了解static。
1、static 修飾局部變量
圖一:test 函數(shù)里面定義的 a 是局部變量,局部變量在棧區(qū)上開辟空間,棧區(qū)的使用特點是進入變量的生命周期時自動為其開辟空間,離開變量的生命周期時自動銷毀對應(yīng)空間,所以這里每次調(diào)用 test 函數(shù)時 a 都會被重新定義并初始化為0,所以屏幕上打印的是10個1;
圖二:我們把 a 用 static 修飾后發(fā)現(xiàn)屏幕打印的是1到10,就好像每次調(diào)用完 test 函數(shù)后 a 并沒有被銷毀,而是繼續(xù)使用,下次調(diào)用 test 函數(shù)時 a 直接在之前的基礎(chǔ)上進行 ++ 操作。
所以 static 修飾局部變量的作用是:改變局部變量的生命周期,本質(zhì)上是改變了局部變量的存儲位置,讓局部變量不再是在棧區(qū)上開辟空間,而是直接在靜態(tài)區(qū)上開辟空間,從而使得局部變量擁有和全局變量一樣的生命周期,即隨著整個程序生成和銷毀。
更深入的理解 static 修飾局部變量的作用:圖三,我們的程序從源文件(.c文件)變成可執(zhí)行程序(.exe文件)需要經(jīng)過編譯鏈接運行三個環(huán)節(jié),而編譯環(huán)節(jié)又分為預(yù)處理、編譯、匯編三個階段,在匯編階段,編譯器會把我們的C語言代碼轉(zhuǎn)換成匯編代碼,而每一條C語言語句都對應(yīng)著多句匯編代碼,然而在圖三中,我們可以觀察到,只有 static int a = 0; 這條語句沒有對應(yīng)的匯編代碼,也就是說,C語言在編譯的時候會直接跳過這條語句。
本質(zhì)上是:在編譯環(huán)節(jié)的編譯階段編譯器就會為被 static 修飾的局部變量分配空間,所以C程序在運行的過程中會直接跳過 static 修飾的語句,也就是說,在第二次及以上甚至第一次調(diào)用 test 函數(shù)時 static int a = 0; 這條語句都不會被執(zhí)行。
補充:內(nèi)存分布:
要弄清楚這個問題,我們首先得知道內(nèi)存布局是怎樣的:
如圖,左邊是內(nèi)存的具體劃分,右邊是內(nèi)存的大概劃分,在C語言階段我們只需要記住右邊的圖就可以了,從圖中我們可以看到,局部變量的內(nèi)存開辟是在棧區(qū)上的,而棧區(qū)的特點是進入代碼塊開辟空間,離開代碼塊釋放空間,所以局部變量的作用域和生命周期只在代碼塊內(nèi),而用static的變量則直接在靜態(tài)區(qū)開辟空間,所以變量的生命周期得到延長。
2、static修飾全局變量
圖一圖二對比分析:我在Add.c中定義了一個全局變量g_val,因為全局變量具有外部鏈接屬性,所以我只需要在test.c中對g_val進行聲明之后就可以正常使用了,但是當我用 static 來修飾g_val時,我們發(fā)現(xiàn),編譯器說g_val是無法解析的外部符號;
所以 static 修飾全局變量的作用是:改變了全局變量的外部鏈接屬性(可以在其他源文件內(nèi)被訪問),使其變成內(nèi)部連接屬性(只能在本文件內(nèi)部被訪問),給我們的感覺是全局變量的作用域變小了。
3、static修飾函數(shù)
圖一圖二對比分析:這里和 static 修飾全局變量非常類似,我在Add.c中定義了一個Add函數(shù),因為函數(shù)也具有外部鏈接屬性,所以我只需要在test.c中對Add函數(shù)進行聲明之后就可以正常使用了,但是當我用 static 來修飾Add函數(shù)時,我們發(fā)現(xiàn),編譯器說Add是無法解析的外部符號;
所以 static 修飾函數(shù)的作用是:改變了函數(shù)的外部鏈接屬性(可以在其他源文件內(nèi)被訪問),使其變成內(nèi)部連接屬性(只能在本文件內(nèi)部被訪問),給我們的感覺是函數(shù)的作用域變小了。
四、總結(jié)
- 1、 全局變量和函數(shù)是可以跨文件訪問的,因為有一定規(guī)模的項目,一定是多文件的,多個文件之間,后續(xù)一定要進行數(shù)據(jù)“交互”(test.h test.c main.c) ,如果不能跨文件訪問,數(shù)據(jù)"交互"成本會非常高,所以C語言在設(shè)計的時候就規(guī)定了全局變量和函數(shù)可以跨文件訪問
- 2、 static 修飾局部變量的作用:改變局部變量的生命周期,本質(zhì)上是改變了局部變量的存儲位置,讓局部變量不再是在棧區(qū)上開辟空間,而是直接在靜態(tài)區(qū)上開辟空間,從而使得局部變量擁有和全局變量一樣的生命周期,即隨著整個程序生成和銷毀。
- 3、static 修飾全局變量的作用:改變了全局變量的外部鏈接屬性(可以在其他源文件內(nèi)被訪問),使其變成內(nèi)部連接屬性(只能在本文件內(nèi)部被訪問)。
- 4、static 修飾函數(shù)的作用是:改變了函數(shù)的外部鏈接屬性(可以在其他源文件內(nèi)被訪問),使其變成內(nèi)部連接屬性(只能在本文件內(nèi)部被訪問)。
到此這篇關(guān)于全面了解C語言 static 關(guān)鍵字的文章就介紹到這了,更多相關(guān)C語言 static 關(guān)鍵內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)與Lua相互調(diào)用的示例詳解
這篇文章主要為大家詳細介紹了C++實現(xiàn)與Lua相互調(diào)用的方法,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下2023-03-03學(xué)好C++必須做到的50條 絕對經(jīng)典!
學(xué)好C++必須做到的50條,絕對經(jīng)典!想要學(xué)好C++的朋友一定要認真閱讀本文,更要做到以下50條2016-09-09C++深入學(xué)習(xí)之徹底理清重載函數(shù)匹配
C++ 不允許變量重名,但是允許多個函數(shù)取相同的名字,只要參數(shù)表不同即可,這叫作函數(shù)的重載,下面這篇文章主要給大家介紹了關(guān)于C++深入學(xué)習(xí)之徹底理清重載函數(shù)匹配的相關(guān)資料,需要的朋友可以參考下2019-01-01C語言驅(qū)動開發(fā)之通過ReadFile與內(nèi)核層通信
驅(qū)動與應(yīng)用程序的通信是非常有必要的,內(nèi)核中執(zhí)行代碼后需要將其動態(tài)顯示給應(yīng)用層。為了實現(xiàn)內(nèi)核與應(yīng)用層數(shù)據(jù)交互則必須有通信的方法,微軟為我們提供了三種通信方式,本文先來介紹通過ReadFile系列函數(shù)實現(xiàn)的通信模式2022-09-09OpenCV4.1.0+VisualStudio2019開發(fā)環(huán)境搭建(超級簡單)
這篇文章主要介紹了OpenCV4.1.0+VisualStudio2019開發(fā)環(huán)境搭建(超級簡單),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03