網站首頁 編程語言 正文
一,前言
大家好,歡迎來到C語言深度解析專欄—關鍵字詳解第二篇,在本篇中我們將會對static關鍵字進行詳細介紹,其中要求我們掌握我上一篇中所講到的全局變量、局部變量、作用域以及生命周期的相關概念,如果對這幾個概念比較模糊的同學可以先移步我上一篇博客,下面是博客鏈接。
C語言關鍵字詳解
二、認識多文件
為了理解static修飾函數的作用,我們需要了解多文件的相關內容
1、多文件的創建
這里我先介紹一下頭文件的創建:頭文件的創建與.c文件的創建十分相似,僅僅是在選擇的時候把c++文件改成.h而已
.h:我們稱之為頭文件,一般包含函數聲明,變量聲明,宏定義,頭文件等內容(header)
.c: 我們稱之為源文件,一般包含函數實現,變量定義等 (.c:c語言)
多文件就是在一個.h文件下,包含多個.c文件,比如main.c test1.c test2.c teset3.c … …
2、為什么要有多文件
在一個公司的大型項目中,預期產品所要實現的功能往往是十分復雜的,所以一般都會將功能進行模塊化處理,從而便于我們進行代碼的復用、代碼的修改與維護以及多人協作,自然我們一個程序中就需要多個.c文件
3、為什么要有頭文件
單純的使用源文件,組織項目結構的時候,項目越大越復雜維護成本會變得越來越高!
所以我們在組織項目結構的時候會使用頭文件來減少大型項目的維護成本問題。
補充:頭文件中 #pragma once 的含義
大家在創建一個.h 頭文件的時候會發現編譯器在頭文件的開頭會自動加上 #pragma once
相信有許多小伙伴在曾今或者現在都對這東西有著深深的疑惑,其實它是用來防止頭文件被反復包含的,舉個栗子
如上所示:我在test.h中包含了頭文件<stdio.h>,但是在main.c中我又同時包含了test.h 和 stdio.h ,這就造成了stdio.h被包含了兩次,使得程序在編譯的時候將stdio.h 里面的內容拷貝了兩份,造成代碼冗余,而#pragma once 會檢查該頭文件是否已經被包含,如若是就不在進行拷貝。
防止頭文件反復包含的另一種方法(涉及預處理內容,暫時不講,同學們當作了解即可)
4、多文件在代碼中的具體體現
在上圖中我們在test.c 文件中中定義了一個全局變量和一個函數,然后在test.h文件中對其進行聲明,最后在main.c文件中對全局變量和函數進行打印和調用,我們可以發現,這種做法是可行的,也就是說:全局變量和函數可以跨文件訪問的(這個結論在解釋下文static作用時會被用到)
三、最名不符實的關鍵字 - static
static 整體闡述
上圖是MSDN對static的解釋,翻譯過來就是:修改變量時,static關鍵字指定該變量具有靜態持續時間(在程序開始時分配,在程序結束時釋放),并將其初始化為0,除非指定了其他值。在文件范圍中修改變量或函數時,static關鍵字指定該變量或函數具有內部鏈接(其名稱在聲明它的文件外部不可見)。這段話讀起來沒什么具體的概念,接下來我從static 作用的三個對象來帶大家具體了解static。
1、static 修飾局部變量
圖一:test 函數里面定義的 a 是局部變量,局部變量在棧區上開辟空間,棧區的使用特點是進入變量的生命周期時自動為其開辟空間,離開變量的生命周期時自動銷毀對應空間,所以這里每次調用 test 函數時 a 都會被重新定義并初始化為0,所以屏幕上打印的是10個1;
圖二:我們把 a 用 static 修飾后發現屏幕打印的是1到10,就好像每次調用完 test 函數后 a 并沒有被銷毀,而是繼續使用,下次調用 test 函數時 a 直接在之前的基礎上進行 ++ 操作。
所以 static 修飾局部變量的作用是:改變局部變量的生命周期,本質上是改變了局部變量的存儲位置,讓局部變量不再是在棧區上開辟空間,而是直接在靜態區上開辟空間,從而使得局部變量擁有和全局變量一樣的生命周期,即隨著整個程序生成和銷毀。
更深入的理解 static 修飾局部變量的作用:圖三,我們的程序從源文件(.c文件)變成可執行程序(.exe文件)需要經過編譯鏈接運行三個環節,而編譯環節又分為預處理、編譯、匯編三個階段,在匯編階段,編譯器會把我們的C語言代碼轉換成匯編代碼,而每一條C語言語句都對應著多句匯編代碼,然而在圖三中,我們可以觀察到,只有 static int a = 0; 這條語句沒有對應的匯編代碼,也就是說,C語言在編譯的時候會直接跳過這條語句。
本質上是:在編譯環節的編譯階段編譯器就會為被 static 修飾的局部變量分配空間,所以C程序在運行的過程中會直接跳過 static 修飾的語句,也就是說,在第二次及以上甚至第一次調用 test 函數時 static int a = 0; 這條語句都不會被執行。
補充:內存分布:
要弄清楚這個問題,我們首先得知道內存布局是怎樣的:
如圖,左邊是內存的具體劃分,右邊是內存的大概劃分,在C語言階段我們只需要記住右邊的圖就可以了,從圖中我們可以看到,局部變量的內存開辟是在棧區上的,而棧區的特點是進入代碼塊開辟空間,離開代碼塊釋放空間,所以局部變量的作用域和生命周期只在代碼塊內,而用static的變量則直接在靜態區開辟空間,所以變量的生命周期得到延長。
2、static修飾全局變量
圖一圖二對比分析:我在Add.c中定義了一個全局變量g_val,因為全局變量具有外部鏈接屬性,所以我只需要在test.c中對g_val進行聲明之后就可以正常使用了,但是當我用 static 來修飾g_val時,我們發現,編譯器說g_val是無法解析的外部符號;
所以 static 修飾全局變量的作用是:改變了全局變量的外部鏈接屬性(可以在其他源文件內被訪問),使其變成內部連接屬性(只能在本文件內部被訪問),給我們的感覺是全局變量的作用域變小了。
3、static修飾函數
圖一圖二對比分析:這里和 static 修飾全局變量非常類似,我在Add.c中定義了一個Add函數,因為函數也具有外部鏈接屬性,所以我只需要在test.c中對Add函數進行聲明之后就可以正常使用了,但是當我用 static 來修飾Add函數時,我們發現,編譯器說Add是無法解析的外部符號;
所以 static 修飾函數的作用是:改變了函數的外部鏈接屬性(可以在其他源文件內被訪問),使其變成內部連接屬性(只能在本文件內部被訪問),給我們的感覺是函數的作用域變小了。
四、總結
- 1、 全局變量和函數是可以跨文件訪問的,因為有一定規模的項目,一定是多文件的,多個文件之間,后續一定要進行數據“交互”(test.h test.c main.c) ,如果不能跨文件訪問,數據"交互"成本會非常高,所以C語言在設計的時候就規定了全局變量和函數可以跨文件訪問
- 2、 static 修飾局部變量的作用:改變局部變量的生命周期,本質上是改變了局部變量的存儲位置,讓局部變量不再是在棧區上開辟空間,而是直接在靜態區上開辟空間,從而使得局部變量擁有和全局變量一樣的生命周期,即隨著整個程序生成和銷毀。
- 3、static 修飾全局變量的作用:改變了全局變量的外部鏈接屬性(可以在其他源文件內被訪問),使其變成內部連接屬性(只能在本文件內部被訪問)。
- 4、static 修飾函數的作用是:改變了函數的外部鏈接屬性(可以在其他源文件內被訪問),使其變成內部連接屬性(只能在本文件內部被訪問)。
原文鏈接:https://blog.csdn.net/m0_62391199/article/details/123517767
相關推薦
- 2023-04-12 如何將python代碼打包成pip包(可以pip?install)_python
- 2022-10-18 C#模擬實現鼠標自動點擊與消息發送功能_C#教程
- 2022-03-03 el-form動態表單切換導致的校驗bug(v-if、v-show導致校驗失效的bug)
- 2023-04-13 next 配置全局scss變量、函數
- 2022-07-30 @Autowired實現的原理
- 2022-04-03 C++中的運算符重載詳解_C 語言
- 2022-03-31 C#實現學生檔案查詢_C#教程
- 2022-03-04 scss的calc計算表達式。沒有起作用是怎么回事
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支