C語言深入講解函數(shù)參數(shù)的使用
一、函數(shù)參數(shù)
- 函數(shù)參數(shù)在本質(zhì)上與局部變量相同在棧上分配空間
- 函數(shù)參數(shù)的初始值是函數(shù)調(diào)用時的實參值
- 函數(shù)參數(shù)的求值順序依賴于編譯器的實現(xiàn)
下面看一個函數(shù)參數(shù)的求值順序的示例:
#include <stdio.h> int func(int i, int j) { printf("i = %d, j = %d\n", i, j); return 0; } int main() { int k = 1; func(k++, k++); printf("%d\n", k); return 0; }
輸出結(jié)果如下:
這個示例說明函數(shù)參數(shù)的求值順序依賴于編譯器的實現(xiàn)。
二、程序的順序點
- 程序中存在一定的順序點
- 順序點指的是執(zhí)行過程中修改變量值的最晚時刻
- 在程序到達順序點的時候,之前所做的—切操作必須完成
- 每個完整表達式結(jié)束時,即分號處
- &&,||,?:,以及逗號表達式的每個參數(shù)計算之后
- 函數(shù)調(diào)用時所有實參求值完成后(進入函數(shù)體之前)
下面看一個程序中的順序點示例:
#include <stdio.h> int main() { int k = 2; int a = 1; k = k++ + k++; printf("k = %d\n", k); if( a-- && a ) { printf("a = %d\n", a); } return 0; }
輸出結(jié)果如下:
a-- && a ,對于 && 運算符,每個操作數(shù)都是一個順序點。當程序從左往后執(zhí)行時,a-- 對內(nèi)存的修改必須立即完成,所以 a 就變成了 0。
為什么會輸出 6 呢?下面在 VS2012 里面運行代碼,進行反匯編操作:
這段匯編代碼簡單的來說,就是先進行 + 操作,k = 2 + 2 = 4,然后進行兩次 ++ 操作,所以最終結(jié)果就是 6。
三、小結(jié)-上
- 函數(shù)的參數(shù)在棧上分配空間
- 函數(shù)的實參并沒有固定的計算次序
- 順序點是 C 語言中變量修改的最晚時機
四、調(diào)用約定
函數(shù)參數(shù)的計算次序是依賴編譯器實現(xiàn)的,那么函數(shù)參數(shù)的入棧次序是如何確定的呢?
當函數(shù)調(diào)用發(fā)生時
- 參數(shù)會傳遞給被調(diào)用的函數(shù)
- 而返回值會被返回給函數(shù)調(diào)用者
調(diào)用約定描述參數(shù)如何傳遞到棧中以及棧的維護方式
- 參數(shù)傳遞順序
- 調(diào)用棧清理
調(diào)用約定是預定義的可理解為調(diào)用協(xié)議
調(diào)用約定通常用于庫調(diào)用和庫開發(fā)的時候
- 從右到左依次入棧:_stdcall,_cdecl,_thiscall
- 從左到右依次入棧:_pascal,_fastcall
五、可變參數(shù)
計算平均值時,我們一般可以編寫成這樣:
#include <stdio.h> float average(int array[], int size) { int i = 0; float avr = 0; for(i=0; i<size; i++) { avr += array[i]; } return avr / size; } int main() { int array[] = {1, 2, 3, 4, 5}; printf("%f\n", average(array, 5)); return 0; }
輸出結(jié)果如下:
C語言中可以定義參數(shù)可變的函數(shù)
參數(shù)可變函數(shù)的實現(xiàn)依賴于 stdarg.h 頭文件
- va_list -- 參數(shù)集合
- va_arg -- 取具體參數(shù)值
- va_start -- 標識參數(shù)訪問的開始
- va_end -- 標識參數(shù)訪問的結(jié)束
下面看一個求可變參數(shù)平均值的代碼:
#include <stdio.h> #include <stdarg.h> float average(int n, ...) { va_list args; int i = 0; float sum = 0; va_start(args, n); for(i=0; i<n; i++) { sum += va_arg(args, int); } va_end(args); return sum / n; } int main() { printf("%f\n", average(5, 1, 2, 3, 4, 5)); printf("%f\n", average(4, 1, 2, 3, 4)); return 0; }
輸出結(jié)果如下:
六、可變參數(shù)的限制
- 可變參數(shù)必須從頭到尾按照順序逐個訪問
- 參數(shù)列表中至少要存在一個確定的命名參數(shù)
- 可變參數(shù)函數(shù)無法確定實際存在的參數(shù)的數(shù)量
- 可變參數(shù)函數(shù)無法確定參數(shù)的實際類型
注意:va_arg 中如果指定了錯誤的類型,那么結(jié)果是不可預測的。
七、小結(jié)-下
- 調(diào)用約定指定了函數(shù)參數(shù)的入棧順序以及棧的清理方式
- 可變參數(shù)是 C 語言提供的一種函數(shù)設計技巧
- 可變參數(shù)的函數(shù)提供了一種更方便的函數(shù)調(diào)用方式
- 可變參數(shù)必須順序的訪問,無法直接訪問中間的參數(shù)值
到此這篇關(guān)于C語言深入講解函數(shù)參數(shù)的使用的文章就介紹到這了,更多相關(guān)C語言 函數(shù)參數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言統(tǒng)計一串字符中空格鍵、Tab鍵、回車鍵、字母、數(shù)字及其他字符的個數(shù)(Ctrl+Z終止輸入)
這篇文章主要介紹了C語言統(tǒng)計一串字符中空格鍵、Tab鍵、回車鍵、字母、數(shù)字及其他字符的個數(shù)(Ctrl+Z終止輸入) ,需要的朋友可以參考下2018-03-03C++?Protobuf實現(xiàn)接口參數(shù)自動校驗詳解
用C++做業(yè)務發(fā)開的同學是否還在不厭其煩的編寫大量if-else模塊來做接口參數(shù)校驗呢?今天,我們就模擬Java里面通過注解實現(xiàn)參數(shù)校驗的方式來針對C++?protobuf接口實現(xiàn)一個更加方便、快捷的參數(shù)校驗自動工具,希望對大家有所幫助2023-04-04成員初始化列表與構(gòu)造函數(shù)體中的區(qū)別詳細解析
無論是在構(gòu)造函數(shù)初始化列表中初始化成員,還是在構(gòu)造函數(shù)體中對它們賦值,最終結(jié)果是相同的。不同之處在于,使用構(gòu)造函數(shù)初始化列表的版本初始化數(shù)據(jù)成員,沒有定義初始化列表的構(gòu)造函數(shù)版本在構(gòu)造函數(shù)體中對數(shù)據(jù)成員賦值2013-09-09