目錄
一、auto
1.1.?作用
1.2. 特性
1.3. 代碼示例
二、register
2.1. 作用
2.2. 特性
2.3. 代碼示例
三、static
3.1.? 修飾局部變量
3.2. 修飾全局變量
3.3. 修飾函數
四、extern
4.1. 作用
4.2. 特性
4.3. 代碼示例
五、volatile
5.1. 作用
5.2. 代碼示例
六、總結
接上一篇https://blog.csdn.net/weixin_37800531/article/details/145430862?sharetype=blogdetail&sharerId=145430862&sharerefer=PC&sharesource=weixin_37800531&spm=1011.2480.3001.8118繼續分析。
一、auto
在C語言中,auto
?關鍵字用于修飾局部變量,盡管在實際編程中,我們往往省略這個關鍵字,因為局部變量默認就是自動存儲類型(auto
)的。auto
?關鍵字的主要作用是顯式地表明變量的存儲類型,并幫助程序員更好地理解代碼。
1.1.?作用
- 修飾局部變量,表明該變量是自動存儲類型的。
- 雖然通常省略,但在某些情況下,顯式使用?
auto
?可以提高代碼的可讀性。
1.2. 特性
- 自動分配內存:當函數被調用時,
auto
?變量會在棧上自動分配內存。 - 自動釋放內存:當函數返回時,
auto
?變量所占用的內存會自動釋放,變量失效。 - 生命周期:
auto
?變量的生命周期僅限于定義它的函數或代碼塊內。
1.3. 代碼示例
雖然auto
關鍵字通常被省略,但以下示例可以展示其用法:
#include <stdio.h> void myFunction() { auto int myAutoVar = 10; // 顯式使用 auto 關鍵字,但通常可以省略 printf("Value of myAutoVar: %d\n", myAutoVar); // myAutoVar 在這里有效,但函數返回后將自動釋放內存
} int main() { myFunction(); // printf("Value of myAutoVar: %d\n", myAutoVar); // 錯誤:myAutoVar 在這里無效 return 0;
}
myAutoVar
?是一個?auto
?類型的局部變量,它在?myFunction
?函數內部被定義并初始化。當?myFunction
?被調用時,myAutoVar
?會在棧上分配內存。當?myFunction
?返回時,myAutoVar
?所占用的內存會自動釋放,變量失效。因此,在?main
?函數中嘗試訪問?myAutoVar
?會導致編譯錯誤。
運行結果:?
需要注意的是,由于局部變量默認就是?
auto
?類型的,所以在實際編程中,我們通常會省略?auto
?關鍵字。
二、register
在C語言中,register
?關鍵字用于向編譯器提出建議,希望編譯器能夠將特定的變量存儲在CPU的寄存器中,以便提高對該變量的訪問速度。寄存器是CPU內部的一種高速存儲單元,其訪問速度遠快于內存。因此,如果某個變量被頻繁地讀取,且不會被修改,那么將其存儲在寄存器中可以顯著提高程序的性能。
2.1. 作用
register
?關鍵字的主要作用是向編譯器發出建議,希望編譯器能夠優化變量的存儲位置,將其從內存移動到寄存器中。- 然而,需要注意的是,這只是一個建議,編譯器并不一定會采納。編譯器會根據自身的優化策略、寄存器的可用性以及變量的使用情況來決定是否將變量存儲在寄存器中。
2.2. 特性
-
適用于頻繁讀取且不會被修改的局部變量:由于寄存器的數量有限,且讀寫寄存器需要消耗一定的CPU資源,因此
register
關鍵字最適合用于那些被頻繁讀取且不會被修改的局部變量。 -
編譯器可能會忽略此建議:如前所述,
register
只是一個建議,編譯器并不一定會采納。編譯器會根據實際情況來決定是否將變量存儲在寄存器中。 -
不能用于全局變量和靜態變量:由于全局變量和靜態變量在程序的整個生命周期內都有效,且可能會被多個函數訪問和修改,因此它們不適合存儲在寄存器中。
2.3. 代碼示例
下面是一個使用register
關鍵字的簡單示例:
#include <stdio.h> void count_loops(int n) { register int i; // 建議編譯器將i存儲在寄存器中 for (i = 0; i < n; i++) { // 循環體為空,僅用于演示 } printf("Loop count: %d\n", i); // 此時i的值應為n
} int main() { count_loops(1000000); // 調用函數,傳入一個較大的值以演示效果 return 0;
}
使用了register
關鍵字來建議編譯器將循環變量i
存儲在寄存器中。然而,需要注意的是,編譯器可能會忽略這個建議,并將i
存儲在內存中。此外,即使編譯器采納了這個建議,由于寄存器的數量有限,如果程序中使用了大量的register
變量,那么編譯器也可能無法將它們全部存儲在寄存器中。?
運行結果:
在使用
register
關鍵字時,我們需要保持謹慎,并意識到它只是一個建議,而不是一個強制性的要求。同時,我們還需要通過實際的性能測試來驗證編譯器是否采納了我們的建議,并評估其對程序性能的影響。?
三、static
在C語言中,static
?關鍵字有多種用途,包括修飾局部變量、全局變量和函數。
3.1.? 修飾局部變量
當static
修飾局部變量時,它會延長該變量的生命周期至整個程序運行期間,但變量的作用域仍然保持不變,即只能在定義它的函數或代碼塊內部訪問。意味著,即使函數執行完畢,該變量的值也會保留下來,供下次函數調用時使用。
- 代碼示例:
#include <stdio.h> void functionWithStaticVar() { static int count = 0; // 靜態局部變量,只在第一次調用時初始化 count++; printf("Count: %d\n", count);
} int main() { functionWithStaticVar(); // 輸出:Count: 1 functionWithStaticVar(); // 輸出:Count: 2 functionWithStaticVar(); // 輸出:Count: 3 return 0;
}
count
是一個靜態局部變量。每次調用functionWithStaticVar
函數時,count
的值都會遞增,并且在下次函數調用時保留下來。
- 實際運行結果:?
3.2. 修飾全局變量
當static
修飾全局變量時,它會限制該變量的作用域,使其只能在定義它的文件內部訪問。有助于避免不同文件之間的命名沖突。
- 代碼示例:
// file1.c
#include <stdio.h> static int globalVar = 100; // 靜態全局變量,只能在file1.c內部訪問 void printGlobalVar() { printf("GlobalVar in file1.c: %d\n", globalVar);
} // file2.c
#include <stdio.h> // 嘗試訪問file1.c中的globalVar會導致編譯錯誤
// extern int globalVar; // 注釋掉這行以避免編譯錯誤 // void printGlobalVarFromFile2() {
// printf("GlobalVar in file2.c: %d\n", globalVar); // 這行會導致鏈接錯誤
// } int main() { // printGlobalVarFromFile2(); // 注釋掉這行以避免編譯錯誤 printGlobalVar(); // 調用file1.c中的函數來打印globalVar return 0;
}
globalVar
是一個靜態全局變量,它只能在file1.c
內部訪問。如果嘗試在file2.c
中訪問它,會導致編譯或鏈接錯誤。
3.3. 修飾函數
當static
修飾函數時,它會使該函數只能在定義它的文件內部使用,防止外部鏈接。有助于隱藏函數的實現細節,減少命名沖突的可能性。
- 代碼示例:
// file1.c
#include <stdio.h> static void internalFunction() { printf("This is an internal function in file1.c\n");
} void externalFunction() { internalFunction(); // 調用內部函數 printf("This is an external function in file1.c\n");
} // file2.c
#include <stdio.h> // 嘗試調用file1.c中的internalFunction會導致鏈接錯誤
// void callInternalFunction() {
// internalFunction(); // 這行會導致鏈接錯誤
// } int main() { externalFunction(); // 調用file1.c中的外部函數 // callInternalFunction(); // 注釋掉這行以避免鏈接錯誤 return 0;
}
internalFunction
是一個靜態函數,它只能在file1.c
內部使用。如果嘗試在file2.c
中調用它,會導致鏈接錯誤。而externalFunction
是一個外部函數,它可以在其他文件中被調用。
四、extern
在C語言編程中,extern
?關鍵字扮演著至關重要的角色,它允許我們聲明在其他文件中定義的變量或函數,從而實現跨文件的資源共享。這是模塊化編程的基礎,使得我們可以將程序拆分為多個文件,每個文件負責特定的功能或數據結構,然后在需要時通過?extern
?聲明來訪問這些外部定義的資源。
4.1. 作用
extern
?關鍵字的主要作用是聲明一個變量或函數是在其他文件中定義的,這樣在當前文件中就可以訪問到這個變量或函數。它是實現跨文件鏈接和訪問的關鍵機制。
4.2. 特性
- 跨文件訪問:
extern
?允許我們訪問在其他文件中定義的變量或函數。 - 聲明順序:在使用?
extern
?聲明的變量或函數之前,編譯器需要知道它們的存在。因此,extern
?聲明通常放在文件的開頭部分,或者在變量或函數被實際使用之前。 - 模塊化編程:
extern
?是模塊化編程的基礎,使得我們可以將程序拆分為多個獨立的文件,每個文件都可以定義自己的變量和函數,并通過?extern
?聲明來訪問其他文件中的資源。
4.3. 代碼示例
下面是一個簡單的示例,展示如何使用?extern
?關鍵字來實現跨文件訪問變量和函數。
- file1.c
#include <stdio.h> // 定義一個全局變量
int globalVar = 42; // 定義一個函數
void printGlobalVar() { printf("GlobalVar in file1.c: %d\n", globalVar);
}
- file1.h
// 在頭文件中使用 extern 來聲明 file1.c 中定義的變量和函數
extern int globalVar;
extern void printGlobalVar();
- ?file2.c
#include <stdio.h>
#include "file1.h" // 包含 file1.c 的頭文件以訪問其聲明的變量和函數 int main() { // 訪問 file1.c 中定義的全局變量 printf("Accessing globalVar from file2.c: %d\n", globalVar); // 調用 file1.c 中定義的函數 printGlobalVar(); return 0;
}
?
file1.c
?定義了一個全局變量?globalVar
?和一個函數?printGlobalVar()
。然后,在?file1.h
?中使用?extern
?關鍵字來聲明這些變量和函數,以便在其他文件中訪問它們。最后,在?file2.c
?中,我們包含了?file1.h
?頭文件,從而能夠訪問?file1.c
?中定義的變量和函數。
通過這種方式,我們可以將程序拆分為多個文件,每個文件負責特定的功能或數據結構,然后通過?extern
?聲明和頭文件來實現跨文件的資源共享和訪問。這是模塊化編程的核心思想之一。
五、volatile
volatile
?關鍵字在C/C++等編程語言中用于告訴編譯器,某個變量的值可能會在程序的控制流之外被改變。這通常發生在硬件訪問、多線程編程或中斷服務程序中。使用?volatile
?可以防止編譯器對該變量進行優化,從而確保每次訪問該變量時都能讀取其最新的值。
5.1. 作用
-
防止優化:編譯器在優化代碼時,可能會將變量的值緩存在寄存器中,以減少對內存的訪問。如果變量被聲明為?
volatile
,編譯器就不會進行這種優化,而是每次訪問該變量時都直接從內存中讀取其值。 -
硬件訪問:在嵌入式系統編程中,
volatile
?常用于訪問硬件寄存器的值。這些寄存器的值可能會由硬件本身或其他外部設備改變,因此需要使用?volatile
?來確保每次都能讀取到最新的值。 -
多線程編程:在多線程環境中,一個線程可能會修改另一個線程中的變量。雖然C/C++標準并沒有將?
volatile
?定義為線程之間的同步機制,但在某些平臺上,使用?volatile
?可以防止編譯器對共享變量的優化,從而增加線程間通信的可靠性(盡管這不是跨平臺或標準的方法,通常應使用同步原語如互斥鎖)。 -
中斷服務程序:在中斷服務程序中,全局變量的值可能會由中斷處理程序改變。使用?
volatile
?可以確保主程序在訪問這些變量時能夠讀取到最新的值。
5.2. 代碼示例
示例1:硬件寄存器訪問
#include <stdint.h> // 假設有一個硬件寄存器的地址是0x40000000
#define HARDWARE_REGISTER *((volatile uint32_t *)0x40000000) int main() { // 讀取硬件寄存器的值 uint32_t value = HARDWARE_REGISTER; // 對寄存器進行寫操作 HARDWARE_REGISTER = 0xDEADBEEF; // 再次讀取寄存器的值 value = HARDWARE_REGISTER; return 0;
}
HARDWARE_REGISTER
?是一個宏,它定義了一個指向硬件寄存器地址的?volatile
?指針。確保了每次訪問?HARDWARE_REGISTER
?時都會直接從硬件寄存器中讀取或寫入值。
示例2:多線程編程中的共享變量
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // 注意:這不是跨平臺或標準的方法來實現線程間同步
volatile int shared_variable = 0; void *thread_function(void *arg) { // 模擬一些工作 sleep(1); // 修改共享變量的值 shared_variable = 1; return NULL;
} int main() { pthread_t thread; pthread_create(&thread, NULL, thread_function, NULL); // 等待線程完成工作 while (shared_variable == 0) { // 這里可能會進行忙等待,但這不是推薦的做法 // 在實際應用中,應該使用條件變量、信號量等同步原語 } printf("Shared variable has been changed by the thread.\n"); pthread_join(thread, NULL); return 0;
}
shared_variable
?是一個?volatile
?變量,它在多線程環境中被共享。雖然?volatile
?在這里可以防止編譯器對?shared_variable
?的優化,但它并不能保證線程之間的同步。在實際應用中,應該使用互斥鎖、條件變量等同步原語來確保線程之間的正確同步。
volatile
?并不提供原子性保證,即它不能保證對?volatile
?變量的讀寫操作是原子的。在多線程環境中,即使使用了?volatile
,也可能需要額外的同步機制來確保對共享變量的正確訪問。
六、總結
C語言共有32個關鍵字。這些關鍵字根據作用可以分為以下四類:
- 數據類型關鍵字(12個):用于聲明變量的數據類型。包括char、double、float、int、long、short、signed、unsigned、struct、union、enum、void等。
- 控制語句關鍵字(12個):用于控制程序的流程。包括for、do、while、break、continue、if、else、goto、switch、case、default、return等。
- 存儲類型關鍵字(4個或5個):用于聲明變量的存儲類型。常見的包括auto、extern、static、register等,有時也將typedef視為存儲類型關鍵字的一種,盡管其主要功能是為數據類型取別名。
- 其他關鍵字(3個或4個):包括const(聲明只讀變量)、sizeof(計算數據類型長度)、volatile(說明變量在程序執行中可被隱含地改變),有時不包括typedef,因其主要功能是數據類型定義。
這些關鍵字在C語言編程中具有重要的作用,是構成C語言程序的基本元素之一。掌握這些關鍵字的使用方法和注意事項,對于學習C語言和進行C語言編程至關重要。