【C語言入門】解鎖核心關鍵字的終極奧秘與實戰應用(三)

目錄

一、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語言編程至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/894507.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/894507.shtml
英文地址,請注明出處:http://en.pswp.cn/news/894507.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Kafka分區策略實現

引言 Kafka 的分區策略決定了生產者發送的消息會被分配到哪個分區中&#xff0c;合理的分區策略有助于實現負載均衡、提高消息處理效率以及滿足特定的業務需求。 輪詢策略&#xff08;默認&#xff09; 輪詢策略是 Kafka 默認的分區策略&#xff08;當消息沒有指定鍵時&…

c++ stl 遍歷算法和查找算法

概述&#xff1a; 算法主要由頭文件<algorithm> <functional> <numeric> 提供 <algorithm> 是所有 STL 頭文件中最大的一個&#xff0c;提供了超過 90 個支持各種各樣算法的函數&#xff0c;包括排序、合并、搜索、去重、分解、遍歷、數值交換、拷貝和…

2.2 實現雙向鏈表的快速排序

實現一個雙向鏈表的快速排序。 1>程序代碼 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h>…

力扣動態規劃-19【算法學習day.113】

前言 ###我做這類文章一個重要的目的還是記錄自己的學習過程&#xff0c;我的解析也不會做的非常詳細&#xff0c;只會提供思路和一些關鍵點&#xff0c;力扣上的大佬們的題解質量是非常非常高滴&#xff01;&#xff01;&#xff01; 習題 1.矩形中移動的最大次數 題目鏈接…

Gurobi基礎語法之 addConstr, addConstrs, addQConstr, addMQConstr

在新版本的 Gurobi 中&#xff0c;向 addConstr 這個方法中傳入一個 TempConstr 對象&#xff0c;在模型中就會根據這個對象生成一個約束。更重要的是&#xff1a;TempConstr 對象可以傳給所有addConstr系列方法&#xff0c;所以下面先介紹 TempConstr 對象 TempConstr TempC…

五子棋對弈

問題描述 "在五子棋的對弈中&#xff0c;友誼的小船說翻就翻&#xff1f;" 不&#xff01;對小藍和小橋來說&#xff0c;五子棋不僅是棋盤上的較量&#xff0c;更是心與心之間的溝通。這兩位摯友秉承著"友誼第一&#xff0c;比賽第二"的宗旨&#xff0c;決…

使用 HTTP::Server::Simple 實現輕量級 HTTP 服務器

在Perl中&#xff0c;HTTP::Server::Simple 模塊提供了一種輕量級的方式來實現HTTP服務器。該模塊簡單易用&#xff0c;適合快速開發和測試HTTP服務。本文將詳細介紹如何使用 HTTP::Server::Simple 模塊創建和配置一個輕量級HTTP服務器。 安裝 HTTP::Server::Simple 首先&…

在AI技術深度滲透的背景下,2025年傳媒互聯網行業的哪些細分場景和產品形態將迎來爆發式增長?

一、AI技術重構傳媒互聯網行業版圖&#xff1a;從底層邏輯到應用場景 近年來&#xff0c;AI技術已從實驗室走向商業化落地&#xff0c;而傳媒互聯網行業因其龐大的用戶基數、高頻交互場景和豐富的數據積累&#xff0c;成為AI應用的主戰場。根據華源證券最新行業周報&#xff0…

Docker Hub 鏡像 Pull 失敗的解決方案

目錄 引言一、問題二、原因三、解決方法四、參考文獻 引言 在云原生技術火熱的當下&#xff0c;Docker可謂是其基礎&#xff0c;由于其簡單以及方便性&#xff0c;讓開發人員不必再為環境配置問題而傷腦筋&#xff0c;因為可將其看作一個虛擬機程序去理解。所以掌握好它可謂是…

neo4j-community-5.26.0 create new database

1.edit neo4j.conf 把 # The name of the default database initial.dbms.default_databasehonglouneo4j # 寫上自己的數據庫名稱 和 # Name of the service #5.0 server.windows_service_nameneo4j #4.0 dbms.default_databaseneo4j #dbms.default_databaseneo4jwind serve…

unity實現回旋鏢函數

最近學習unity2D&#xff0c;想實現一個回旋鏢武器&#xff0c;發出后就可以在角色周圍回旋。 一、目標 1.不是一次性的&#xff0c;扔出去、返回、沒有了&#xff1b;而是扔出去&#xff0c;返回到角色后方相同距離&#xff0c;再次返回&#xff1b;再次返回&#xff0c;永遠…

【C++基礎】字符串/字符讀取函數解析

最近在學C以及STL&#xff0c;打個基礎 參考&#xff1a; c中的char[] ,char* ,string三種字符串變量轉化的兼容原則 c讀取字符串和字符的6種函數 字符串結構 首先明確三種字符串結構的兼容關系&#xff1a;string>char*>char [] string最靈活&#xff0c;內置增刪查改…

求一個數的數根(高精度)

上一期我們講的是求一個數的數根&#xff0c;和本期唯一不同的是&#xff0c;數據范圍不同了&#xff0c;上一期這個數是小于等于10的18次方的&#xff0c;這一期是小于等于10的1000次方的&#xff0c;開一個變量&#xff1f;肯定不行&#xff0c;我們需要再開一個數組&#xf…

SpringBoot源碼解析(九):Bean定義接口體系

SpringBoot源碼系列文章 SpringBoot源碼解析(一)&#xff1a;SpringApplication構造方法 SpringBoot源碼解析(二)&#xff1a;引導上下文DefaultBootstrapContext SpringBoot源碼解析(三)&#xff1a;啟動開始階段 SpringBoot源碼解析(四)&#xff1a;解析應用參數args Sp…

Vue 3 30天精進之旅:Day 13 - 路由守衛

在構建單頁面應用時&#xff0c;路由守衛是一個非常重要的概念。它允許我們在路由進入或離開時執行一些操作&#xff0c;比如驗證用戶權限、處理數據加載、執行導航確認等。Vue Router提供了多種類型的路由守衛&#xff0c;使我們能夠靈活地控制路由的行為。在今天的學習中&…

【TypeScript】基礎:數據類型

文章目錄 TypeScript一、簡介二、類型聲明三、數據類型anyunknownnervervoidobjecttupleenumType一些特殊情況 TypeScript 是JavaScript的超集&#xff0c;代碼量比JavaScript復雜、繁多&#xff1b;但是結構更清晰 一、簡介 為什么需要TypeScript&#xff1f; JavaScript的…

C++模板編程——可變參函數模板

目錄 1. 可變參函數模板基本介紹 2. 參數包展開——通過遞歸函數 3. 參數包展開——通過編譯期間if語句(constexpr if) 4. 重載 5. 后記 進來看的小伙伴們應該對C中的模板有了一定了解&#xff0c;下面給大家介紹一下可變參函數模板。過于基礎的概念將不仔細介紹。 1. 可變…

ChatGPT-4o和ChatGPT-4o mini的差異點

在人工智能領域&#xff0c;OpenAI再次引領創新潮流&#xff0c;近日正式發布了其最新模型——ChatGPT-4o及其經濟實惠的小型版本ChatGPT-4o Mini。這兩款模型雖同屬于ChatGPT系列&#xff0c;但在性能、應用場景及成本上展現出顯著的差異。本文將通過圖文并茂的方式&#xff0…

三數之和(15)

15. 三數之和 - 力扣&#xff08;LeetCode&#xff09; 可以一起總結的題目&#xff1a;三角形的最大周長&#xff08;976&#xff09;-CSDN博客 解法&#xff1a; class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {vector…

2025最新源支付V7全套開源版+Mac云端+五合一云端

2025最新源支付V7全套開源版Mac云端五合一云端 官方1999元&#xff0c; 最新非網上那種功能不全帶BUG開源版&#xff0c;可以自己增加授權或二開 擁有卓越的性能和豐富的功能。它采用全新輕量化的界面UI&#xff0c;讓您能更方便快捷地解決知識付費和運營贊助的難題 它基于…