C語言 | 函數核心機制深度解構:從底層架構到工程化實踐

個人主頁-愛因斯晨

文章專欄-C語言

在這里插入圖片描述

引言

最近偷懶了,迷上了三國和李賀。給大家分享一下最喜歡的一句詩:吾不識青天高黃地厚,唯見月寒日暖來煎人壽。我還不是很理解27歲的李賀,如何寫出如此絕筆。

正文開始,今天我們來探討一下關于C語言中的函數部分

一、函數的概念:代碼的 “模塊化” 基石

1.1 函數的定義與意義

  • 定義:函數是一段可重復使用的代碼塊,具有輸入(參數)處理邏輯(函數體)**和**輸出(返回值)
  • 意義:
    • 復用性:避免重復編寫相同邏輯(如多次計算最大值,只需調用 max 函數)。
    • 可讀性:通過函數名(如 sortArray)直觀理解功能,降低代碼復雜度。
    • 可維護性:修改函數內部邏輯時,只需更新一處,不影響其他調用處。

1.2 函數的基本結構

返回類型 函數名(參數列表) {// 函數體:實現具體功能return 返回值; // 非void類型必須返回對應類型的值
}
  • 示例:計算兩數之和

    int add(int a, int b) { // 返回int,參數a、b為intreturn a + b; // 返回和
    }
    

二、庫函數:“開箱即用” 的工具集

關于庫函數和其他語言中封裝的函數在上篇文章中已經講到了,詳情請看[從庫函數到API接口,深挖不同語言背后的“封裝”與“調用”思想-CSDN博客]()

2.1 庫函數的分類與頭文件

  • 標準庫:C 語言內置的函數集合,分為:

    • 輸入輸出(stdio.hprintf(輸出)、scanf(輸入)。
    • 字符串處理(string.hstrlen(字符串長度)、strcpy(字符串復制)。
    • 數學運算(math.hsqrt(開平方)、pow(冪運算)。
    • 內存管理(stdlib.hmalloc(動態內存分配)、free(釋放內存)。
  • 頭文件:包含庫函數的聲明

    (告訴編譯器函數的存在、參數和返回值)。使用庫函數前必須包含對應頭文件,例如:

    #include <stdio.h> // 包含printf的聲明
    int main() {printf("Hello, World!"); // 調用庫函數return 0;
    }
    

2.2 庫函數的使用步驟(以 fgets 為例)

  1. 查閱文檔fgets 從文件中讀取字符串,原型為 char *fgets(char *s, int size, FILE *stream);

  2. 包含頭文件#include <stdio.h>fgets 聲明在此頭文件中)。

  3. 調用函數

    #include <stdio.h>
    int main() {char str[100];fgets(str, 100, stdin); // 從標準輸入(鍵盤)讀取最多99個字符(含'\0')printf("輸入內容:%s", str);return 0;
    }
    
  4. 注意事項:

    • size 參數需小于數組長度(避免緩沖區溢出)。
    • 返回值為 NULL 表示讀取失敗(如文件結束)。

三、自定義函數:“按需定制” 的代碼塊

3.1 函數定義的詳細語法

  • 返回類型:
    • void:無返回值(如僅打印信息的函數)。
    • 基本類型(intfloat 等):返回對應類型的值。
  • 參數列表:
    • 無參數void func()func()(C99 后允許省略 void)。
    • 有參數int add(int a, int b)ab 為形參,接收實參的值)。
  • 函數體:包含實現邏輯的代碼,可使用 return 提前結束函數(void 函數用 return;)。

3.2 示例:實現 “判斷素數” 函數

#include <stdio.h>
#include <math.h>// 自定義函數:判斷n是否為素數(返回1是,0否)
int isPrime(int n) {if (n <= 1) return 0; // 1及以下不是素數for (int i=2; i<=sqrt(n); i++) { // 優化:只需檢查到平方根if (n % i == 0) return 0; // 能整除,不是素數}return 1; // 是素數
}int main() {int num;printf("輸入一個整數:");scanf("%d", &num);if (isPrime(num)) {printf("%d是素數\n", num);} else {printf("%d不是素數\n", num);}return 0;
}
  • 解釋:
    • 形參 n 接收實參(用戶輸入的 num)。
    • 通過循環判斷是否有因數,提前返回結果(提高效率)。

四、形參和實參:“值的傳遞與拷貝”

4.1 實參(實際參數)

  • 定義:調用函數時傳遞的具體值或變量(如 isPrime(num) 中的 num)。
  • 特點:
    • 可以是常量isPrime(7))、變量isPrime(num))、表達式isPrime(2+3))。
    • 傳遞方式:值傳遞(形參是實參的拷貝,修改形參不影響實參,除非傳遞地址)。

4.2 形參(形式參數)

  • 定義:函數定義時占位的參數(如 isPrime(int n) 中的 n)。
  • 特點:
    • 函數調用時分配內存,調用結束后釋放(形參是臨時變量)。
    • 值傳遞本質:形參是實參的副本(如 nnum 的拷貝,修改 n 不影響 num)。

4.3 地址傳遞(突破值傳遞限制)

// 交換兩數(通過地址傳遞,修改實參)
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int main() {int x=10, y=20;swap(&x, &y); // 實參是x、y的地址(傳遞指針)printf("x=%d, y=%d\n", x, y); // 輸出x=20, y=10(實參被修改)return 0;
}
  • 解釋:
    • 形參 *a*b 接收實參的地址(&x&y),通過解引用(*a)直接修改原變量的值。
    • 這是 值傳遞的特殊情況(傳遞地址,實現 “引用傳遞” 效果)。

五、return 語句:“函數的出口與結果”

5.1 return 的兩種用法

  • 返回值:給調用者一個結果(如 return a + b; 返回和)。
  • 結束函數:提前退出函數(如 void 函數中的 return;,跳過后續代碼)。

5.2 規則與示例

  • void 函數

    void printMessage() {printf("Hello!\n");return; // 可省略(函數體結束自動返回)
    }
    
  • 非 void 函數

    int max(int a, int b) {if (a > b) return a; // 返回a,結束函數return b; // 必有一個執行(確保返回值)
    }
    
  • 錯誤處理

    int divide(int a, int b) {if (b == 0) {printf("除數不能為0!\n");return -1; // 錯誤碼(調用者根據返回值判斷是否出錯)}return a / b;
    }
    

六、數組作為函數參數:“傳遞指針與內存”

6.1 數組傳參的本質

  • 數組名作為參數時,傳遞的是首元素的地址(即指針),函數內對數組的修改會影響原數組(因為操作同一塊內存)。

  • 語法

    void printArray(int arr[], int size) { // 等價于int *arrfor (int i=0; i<size; i++) {printf("%d ", arr[i]); // 等價于*(arr+i)}
    }
    

6.2 示例:數組排序(冒泡排序)

#include <stdio.h>void bubbleSort(int arr[], int size) {for (int i=0; i<size-1; i++) {for (int j=0; j<size-i-1; j++) {if (arr[j] > arr[j+1]) { // 交換相鄰元素int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}int main() {int nums[] = {5, 3, 8, 1, 2};int size = sizeof(nums) / sizeof(nums[0]); // 計算數組長度bubbleSort(nums, size); // 傳遞數組名(首地址)和長度for (int i=0; i<size; i++) {printf("%d ", nums[i]); // 輸出1 2 3 5 8(原數組已排序)}return 0;
}
  • 注意:函數無法自動獲取數組長度(需手動傳遞 size),因為形參 arr 是指針(丟失長度信息)。

七、函數調用:嵌套與鏈式

7.1 嵌套調用(函數內調用其他函數)

void printHeader() {printf("===== 歡迎使用系統 =====\n");
}void printMenu() {printHeader(); // 嵌套調用printHeaderprintf("1. 登錄\n2. 注冊\n3. 退出\n");
}int main() {printMenu(); // 輸出:===== 歡迎使用系統 ===== → 菜單選項return 0;
}

7.2 鏈式訪問(函數返回值作為參數)

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }int main() {// 先算add(2,3)=5,再算mul(5,4)=20(鏈式調用)int result = mul(add(2, 3), 4); printf("結果:%d\n", result); // 輸出20return 0;
}

八、函數的聲明與定義:“多文件開發”

8.1 單個文件中的聲明

  • 定義在前:直接調用(無需聲明)。

  • 定義在后:需先聲明(告訴編譯器函數存在)。

    int add(int, int); // 聲明(參數名可省略,只寫類型)
    int main() {int res = add(3,5); // 調用時,編譯器通過聲明知道add存在return 0;
    }
    int add(int a, int b) { return a + b; } // 定義在后
    

8.2 多文件開發(模塊化)

  • 步驟:

    1. 創建頭文件(func.h):聲明函數

      #ifndef FUNC_H
      #define FUNC_H
      int add(int a, int b); // 聲明
      #endif
      
    2. 創建源文件(func.c):定義函數

      #include "func.h" // 包含頭文件(雙引號表示當前目錄)
      int add(int a, int b) { return a + b; } // 定義
      
    3. 主文件(main.c):調用函數

      #include <stdio.h>
      #include "func.h" // 包含頭文件,獲取聲明
      int main() {printf("%d\n", add(3,5)); // 調用func.c中的addreturn 0;
      }
      
  • 編譯:需同時編譯 main.cfunc.c(如 gcc main.c func.c -o main)。

8.3 static 關鍵字:“限制作用域”

  • 靜態函數(static 修飾函數)

    • 作用:僅當前文件可見(其他文件無法調用,避免命名沖突)。

    • 示例(func.c

      static int add(int a, int b) { return a + b; } // main.c調用會報錯(未定義)
      
  • 靜態變量

    • 靜態局部變量(函數內)

      生命周期為程序運行期(保留值,如計數器)。

      void count() {static int num = 0; // 第一次調用初始化,后續保留值num++;printf("第%d次調用\n", num);
      }
      // 調用:count() → 第1次,count() → 第2次(num保留1)
      
    • 靜態全局變量(文件內,函數外):僅當前文件可見(同文件內函數可訪問,其他文件不可見)。

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

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

相關文章

uniapp真機調試“沒有檢測到設備,請插入設備或啟動模擬器后點擊刷新再試”

當真機調試&#xff0c;運行到安卓 APP基座 時&#xff0c;有時會檢測不到設備&#xff0c;顯示下面的問題&#xff1a;此時&#xff0c;可以通過下面的幾種方法進行排查&#xff1a;1.在手機中找到“開發者選項”選項&#xff08;可在設置中搜索&#xff0c;如搜索不到&#x…

使用langchain連接llama.cpp部署的本地deepseek大模型開發簡單的LLM應用

langchain是一個基于python實現的開源LLM開發框架&#xff0c;llama.cpp是一個基于C框架可以在本地部署大模型并開放服務端接口開放給外部應用使用。 本文結合langchain和llama.cpp&#xff0c;在本地部署輕量級的deepseek大模型&#xff0c;并構建一個簡單的鏈式LLM應用&…

Serverless 數據庫來了?無服務器數據庫 vs 傳統數據庫有何不同?

隨著云計算技術的迅猛發展&#xff0c;無服務器&#xff08;Serverless&#xff09;架構逐漸成為一種主流趨勢。其中&#xff0c;Serverless 數據庫作為云原生應用的重要組成部分&#xff0c;為開發者提供了前所未有的靈活性和成本效益。相比傳統的數據庫管理方式&#xff0c;S…

【讀書筆記】如何畫好架構圖:架構思維的三大底層邏輯

【讀書筆記】如何畫好架構圖&#xff1a;架構思維的三大底層邏輯 架構圖并非技術人的“畫功比拼”&#xff0c;而是一個團隊、一個系統、一次項目從混沌走向清晰的關鍵抓手。它是系統的視覺語言&#xff0c;是讓技術人員、產品經理、運營甚至老板都能站在統一上下文下討論的“…

Maven 編譯過程中發生了 Java Heap Space 內存溢出(OutOfMemoryError)

這個是我最近遇到的&#xff0c;因為本人最近換了電腦&#xff0c;這個電腦的前任是配置好了環境&#xff0c;但是當我用這個環境去做另外一個項目的時候&#xff0c;在maven構建war和jar包的時候&#xff0c;報了這個內存溢出mvn clean install 就給我報錯了[ERROR] Failed to…

C++ 模板參數展開

C 模板參數展開一、獲取可變參數大小二、通過模版循環繼承的方式來展開可變參數三、改用Using去實現循環繼承一、獲取可變參數大小 背景&#xff1a; FLen<int, char, long> Len; 我想要獲取模板參數類型的總大小 template<typename T,typename ...ParamTypes> c…

零基礎入門物聯網-遠程門禁開關:云平臺創建

一、 onenet云平臺注冊創建 遠程開關的信息傳輸依賴云平臺&#xff0c;本教程以 OneNET - 中國移動物聯網開放平臺為例進行操作&#xff0c;具體步驟如下&#xff1a; 1、平臺賬號創建 點擊 OneNET - 中國移動物聯網開放平臺進入官網 點擊頁面中的 “登錄” 按鈕&#xff0c;…

html頁面,當鼠標移開A字標就隱藏顏色框

html頁面代碼&#xff1a;<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>顏色選擇器</title><style>body {font-family: "Microsoft YaHei", sans-serif;padding: 20px;}.c…

保姆級搭建harbor私有倉庫與docker-ce教程與使用教程

搭建harbor倉庫[rootharbor ~]# vim cat /etc/host192.168.121.12 harbor[rootharbor ~]# vim /etc/hostnameharbor導入 harbor 項目鏡像[rootharbor ~]# tar -zxf harbor-v2.9.2.tgz -C /usr/local/[rootharbor ~]# cd /usr/local/harbor[rootharbor harbor]# docker load -i…

【Linux】Rocky Linux 安裝 Docker 與 Docker-Compose

Docker 安裝步驟 1. 安裝必要的軟件包 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo2. 安裝Docker sudo yum install docker-ce docker-ce-cli containerd.io如果出現 SSL 證書錯誤&#xf…

揭示獨特模式:Elasticsearch 中 significant terms 聚合指南

作者&#xff1a;來自 Elastic Alexander Dvila 了解如何使用 significant terms 聚合來發現你數據中的洞察。 更多閱讀&#xff1a;Elasticsearch&#xff1a;significant terms aggregation Elasticsearch 擁有大量新功能&#xff0c;可以幫助你為你的使用場景構建最佳搜索解…

pandas.DataFrame中axis參數

明確axis0與axis1的區別和聯系&#xff0c; 假設有一個 DataFrame&#xff1a;indexAB012134axis0&#xff08;沿行方向&#xff09;&#xff1a; 操作會垂直向下進行&#xff0c;對每一列單獨處理。 例如&#xff1a;df.sum(axis0) 會對列 A 和列 B 分別求和&#xff0c;結果是…

深度學習 最簡單的神經網絡 線性回歸網絡

用最簡單的線性模型講清 神經網絡 訓練全流程,讓你 5 分鐘看懂AI 是怎么學會預測的 ?? 1 真實神經元結構 ?? 真實神經元包括: 樹突 接收其他神經元傳來的電信號(輸入)。 細胞核 負責整合輸入信號并產生動作電位。 軸突 傳導動作電位到下一個神經元。 突觸 釋放神經遞質…

k8s Mutating Admission Webhook 實現超賣

目錄 1.什么是 Mutating Admission Webhook&#xff1f; 2.如何用 Mutating Admission Webhook 實現超賣&#xff1f; 3.實現超賣 3.1 理解目標 3.2 前置準備 3.3 開發 Mutating Webhook 3.4 配置 Webhook Server TLS 認證 3.5 注冊 MutatingWebhookConfiguration 3.6…

為 Go-llm-cpp 接入 Web API 接口,創建 Chatbot 聊天機器人

接續上一篇&#xff0c;用 Go 打造本地 LLM 聊天機器人&#xff1a;整合 llm-go 與 go-llama.cpp&#xff0c;此篇開始建構前端與 API 接口 執行環境需求 ? ? Go 1.20 ? ? C toolchain&#xff08;macOS: Xcode Command Line Tools / Linux: g&#xff09; ? ? GGUF 格式…

Docker筆記-Docker Compose

Docker筆記-Docker Compose Compose 是用于定義和運行多容器 Docker 應用程序的工具&#xff0c;通過 Compose 您可以使用 YML 文件來配置應用 程序需要的所有服務。然后&#xff0c;使用一個命令&#xff0c;就可以從 YML 文件配置中創建并啟動所有服務。 Compose 使用的三個步…

n1 armbian 安裝桌面環境并啟用xrdp遠程登錄

armbian-config armbian-software201frpcrootarmbian:~# armbian-software [ STEPS ] Start selecting software [ Current system: ubuntu/noble ]... ──────────────────────────────────────────────────────────…

從傳統到智能:地質災害風險評估、易發性分析與災后重建;AI大語言模型DeepSeek、ChatGPT、GIS、Python和機器學習深度融合

地質災害是指全球地殼自然地質演化過程中&#xff0c;由于地球內動力、外動力或者人為地質動力作用下導致的自然地質和人類的自然災害突發事件。在降水、地震等自然誘因的作用下&#xff0c;地質災害在全球范圍內頻繁發生。我國不僅常見滑坡災害&#xff0c;還包括崩塌、泥石流…

便捷的電腦自動關機輔助工具

軟件介紹 本文介紹的軟件是一款電腦上實用的倒計時和關機助手。 軟件特性 這款關機助手十分貼心&#xff0c;它是一款無需安裝的小軟件&#xff0c;體積僅60KB&#xff0c;不用擔心占用電腦空間&#xff0c;打開即可直接使用。 操作方法 你只需設置好對應的關機時間&#x…

Fiddler-關于抓取Android手機包,安裝證書后頁面加載失敗,提示當前證書不可信存在安全風險的問題

Fiddler-關于抓取Android手機包&#xff0c;安裝證書后頁面加載失敗&#xff0c;提示當前證書不可信存在安全風險的問題Fiddler-關于抓取Android手機包&#xff0c;安裝證書后頁面加載失敗&#xff0c;提示當前證書不可信存在安全風險的問題原因解決方法Fiddler-關于抓取Androi…