網站首頁 編程語言 正文
1. 前言
大家好,我是努力學習游泳的魚。關鍵字,這名字一聽,就很關鍵。而有些關鍵字,你可能不是很了解,更別談使用。所以,這篇文章將帶你見識常見的關鍵字,一起領略它們的風采吧。
2. 什么是關鍵字
C語言提供了豐富的關鍵字,這些關鍵字都是語言本身預先設定好的,
用戶自己是不能創造關鍵字的。
大部分關鍵字會在其他章節介紹,這里僅介紹一些稍微有點難度的關鍵字。
3. extern-聲明外部符號
extern可以用來聲明外部符號,如外部的全局變量和函數。
如我們在test1.c里定義了全局變量aint a = 2022;
我們想在test2.c里使用,就得先用extern聲明一下extern int a;
注意:一般extern是用來聲明外部的全局變量的。因為如果直接寫int a;就不是聲明了,而是定義,會直接創建一個變量a。只有寫extern int a;才是聲明變量a。如果是聲明外部的函數,可以省略掉extern。如直接寫int Add(int, int);和寫extern int Add(int, int);效果是相同的。
4. auto-自動
C語言里的局部變量,進入局部范圍時自動創建,出局部范圍時自動銷毀。這種自動創建,自動銷毀的特性,其實是由于前面省略了關鍵字auto。比如,int a = 0;其實編譯器會處理為auto int a = 0;一般來說,auto會被省略掉。
5. typedef-類型重定義(類型重命名)
typedef關鍵字用于給類型起別名,相當于起了個外號。
比如unsigned int num = 10;如果我們嫌unsigned int這個類型寫起來太麻煩了,可以給它起個別名叫做uint:typedef unsigned int uint;這樣上面的代碼就等價于uint num = 10;
6. register-寄存器
6.1 存儲器
數據的存儲,需要存儲器。常見的存儲器有:
網盤,硬盤,內存,高級緩存,寄存器。
從左到右,速度越快,從而造價越高,從而空間越小。
早期,CPU處理的數據都來自內存。當時,CPU的處理速度和內存的讀寫速度是差不多的。隨著技術的迭代,內存的讀寫速度逐漸跟不上CPU的處理速度,CPU在很大程度上被閑置了。
于是就有了這么一層設計。在內存之上設置讀寫速度更快的高級緩存和寄存器。CPU從寄存器中拿數據,與此同時,寄存器從高級緩存中拿數據,高級緩存從內存中拿數據。如果CPU想要的數據在寄存器中沒有,那就直接從高級緩存中拿數據,如果還沒有再從內存中拿。由于大部分數據都能在寄存器中命中,整體上,處理數據的速度就提升了。
以上,我們能明白一點:
寄存器的讀寫速度是非常快的!
6.2 register關鍵字的作用
如果我們寫int num = 10;num是放在內存中的。如果我們加了個registerregister int num = 10;此時register的作用是建議把num放在寄存器中。注意只是建議,實際是否放在寄存器中取決于編譯器的處理。
7. static-靜態
在C語言中,static有3種用法,分別修飾局部變量,全局變量和函數。
1.修飾局部變量-稱為靜態局部變量
2.修飾全局變量-稱為靜態全局變量
3.修飾函數-稱為靜態函數
7.1 static修飾局部變量
7.1.1 代碼對比
下面代碼的輸出結果是多少呢?
#include <stdio.h>
void test()
{
int a = 5;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
輸出結果:
10個6
為什么呢?test函數被調用了10次,每次都做了同樣一件事,創建a并初始化為5,a自增變成6,打印a(即6)。本質上,每次進入test函數都會創建a,出test函數時都會銷毀a。這是由于局部變量的特性:進入局部范圍創建,出局部范圍銷毀。那么,每次進入test函數創建的都是一個新的a,和之前創建的a沒有任何關系。
明白這點后,再看下面這段代碼,輸出的結果又是多少?
#include <stdio.h>
void test()
{
static int a = 5;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
答案:
輸出6~15。
分析一下:第一次調用test函數時和沒有static相同,創建a并初始化,自增,打印(此時a是6),但第二次調用怎么就打印7了呢?這說明,第二次調用時,a還是上次調用留下來的6,才會自增變成7!也就是說,第一次調用結束后,a并沒有銷毀,第二次調用時依然存在。同理,第二次調用后a也沒有銷毀,第三次調用時a仍是第二次調用留下來的7,然后自增變成8后打印,以此類推。
static修飾局部變量的時候,局部變量就變成了靜態的局部變量,出了局部的范圍,不會銷毀,下一次進入函數依然存在。
7.1.2 原理分析
內存可以分為:棧區,堆區,靜態區,等等。
棧區存儲的是局部變量,函數參數,等等。
堆區是用來動態內存開辟的,與之相關的函數有malloc,realloc,calloc和free等等。
靜態區存儲的是靜態變量和全局變量。
靜態的局部變量出了作用域依然存在,是因為它是存儲在靜態區的。
同樣存儲在靜態區的全局變量,生命周期也很長。
static修飾局部變量時,實際改變的是變量的存儲位置,本來一個局部變量是放在棧區的,被static修飾后放在了靜態區,從而導致,出了作用域依然存在,生命周期并沒有結束。
注意:放在靜態區的變量出了作用域不銷毀,相當于生命周期變長了,但是作用域并沒有發生變化,也就是說,靜態的局部變量仍然只能在它的局部范圍內使用!
靜態區中的數據的生命周期和程序的生命周期是一致的。程序結束,靜態數據的生命周期也就到了。
7.2 static修飾全局變量
7.2.1 代碼對比
我們創建兩個源文件,test1.c和test2.c
在test1.c里定義一個全局變量g_val
// test1.c
int g_val = 2022; // 全局變量,定義在test1.c中
在test2.c內部使用這個全局變量,由于全局變量的作用域是整個工程,所以可以跨源文件使用。但是在使用前需要使用extern聲明,否則會報編譯錯誤。
// test2.c
extern int g_val;
int main()
{
g_val = 2023;
return 0;
}
如果我們在g_val的定義前面加上static會發生什么呢?
// test1.c
static int g_val = 2022; // 全局變量,定義在test1.c中
// test2.c
extern int g_val;
int main()
{
g_val = 2023;
return 0;
}
此時會報鏈接錯誤,因為g_val是定義在test1.c里的靜態全局變量,不能在test2.c內部使用。看來靜態的全局變量不能跨文件使用了。
7.2.2 原理分析
一個全局變量本來是具有外部鏈接屬性的,既能在自己所在的源文件內部使用,也能在其他文件內部使用。
但是被static修飾之后外部鏈接屬性就變成了內部鏈接屬性,只能在自己所在的源文件內部使用,不能在其他文件內部使用了。
使用上感覺作用域變小了。
7.3 static修飾函數
7.3.1 代碼對比
我們在test1.c里定義一個函數
// test1.c
int Add(int x, int y)
{
return x + y;
}
在test2.c內部使用,同理要先聲明(此時可以省略extern),否則會報一個警告。
// test2.c
#include <stdio.h>
extern int Add(int, int); // extern可以省略
int main()
{
int sum = Add(10, 20);
printf("sum = %d\n", sum);
return 0;
}
如果在函數定義前加上static會發生什么呢?
// test1.c
static int Add(int x, int y)
{
return x + y;
}
// test2.c
#include <stdio.h>
extern int Add(int, int); // extern可以省略
int main()
{
int sum = Add(10, 20);
printf("sum = %d\n", sum);
return 0;
}
此時會報鏈接錯誤,因為Add函數是定義在test1.c內部的靜態函數,不能在test2.c內部使用。看來static修飾函數和修飾全局變量類似,靜態的函數也不能跨文件調用。
7.3.2 原理分析
static修飾函數的作用:一個函數本來是具有外部鏈接屬性的,但是被static修飾之后,外部鏈接屬性就變成了內部鏈接屬性,這時這個函數只能在自己所在的源文件內部使用,其他文件是無法使用的。
使用上的感覺好像是作用域變小了。
原文鏈接:https://blog.csdn.net/xiang_bolin/article/details/127143858
相關推薦
- 2022-08-19 Exception evaluating SpringEL expression異常處理
- 2022-07-28 Redis基本數據類型Zset有序集合常用操作_Redis
- 2023-01-17 Qt中控件的函數使用教程分享_C 語言
- 2022-08-04 詳解C++中賦值,關系,函數調用運算符重載的實現_C 語言
- 2022-03-29 python教程之生成器和匿名函數_python
- 2022-09-12 Windows系統下安裝tensorflow的配置步驟_python
- 2022-04-09 SpringBoot 項目在Linux 環境下,日志文件logback撐爆云服務器
- 2023-03-25 Golang實現優雅的將struct轉換為map_Golang
- 最近更新
-
- 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同步修改后的遠程分支