C 语言精髓之变参函数
生活随笔
收集整理的這篇文章主要介紹了
C 语言精髓之变参函数
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
我們以 printf 這個 very 熟悉的函數為例,來分析一下變參函數。先看下 printf 函數的定義:
int printf(const char *fmt, ...){ ? ?int i; ? ?int len; ? ?/* va_list 即 char * */va_list args;va_start(args, fmt); ? ?/* 內部使用了 va_arg() */len = vsprintf(g_PCOutBuf,fmt,args);va_end(args); ? ?for (i = 0; i < strlen(g_PCOutBuf); i++){putc(g_PCOutBuf[i]);} ? ?return len; }什么是變參函數?
可變參數函數的原型聲明為 type VAFunction(type arg1, type arg2, … );
參數可以分為兩部分:個數確定的固定參數和個數可變的可選參數。函數至少需要一個固定參數,固定參數的聲明和普通函數一樣;可選參數由于個數不確定,聲明時用 "..." 表示。固定參數和可選參數共同構成一個函數的參數列表。
printf 函數涉及了以下幾個重要的宏:
typedef char * ? va_list;/** Storage alignment properties*/#define _AUPBND (sizeof (acpi_native_int) - 1) //acpi_native_int 為 4 字節(根據機器字長而定)#define _ADNBND (sizeof (acpi_native_int) - 1) /** Variable argument list macro definitions*/#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))#define va_end(ap) (void) 0#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))分析以下三個宏的作用
1) va_start 宏
作用: 根據 A 取得可變參數表的首指針并賦值給 ap。
原理: 根據最后一個固定參數 A 的地址 + 第一個變參對 A 的偏移地址,然后賦值給 ap,這樣 ap 就是可變參數表的首地址(函數傳遞的參數會從右向左依次入棧,并且 ARM 的棧為降棧,所以參數 A 的地址最低)。
2) va_arg 宏
作用: 指取出當前 ap 所指的可變參數并將 ap 指針指向下一可變參數。
3) va_end 宏
作用: 結束可變參數的獲取,與 va_start 對應使用。
堆棧中,各個函數的分布情況是倒序的,即最后一個參數在列表中地址最高部分,第一個參數在列表地址的最低部分。參數在堆棧中的分布情況如下:*******************最后一個參數 ?倒數第二個參數 ... ? ? ? ? ?第一個參數 ? ? 函數返回地址 ? 函數代碼段 ? ? *******************得到可變參數個數的三種辦法:1) 函數的第一個參數,指定后續的參數個數,如 func(int num,...);2) 根據隱含參數,判斷參數個數,如 printf 系列的,通過字符串中 % 的個數判斷;3) 特殊情況下(如參數都是不大于 0xFFFF 的 int),可以一直向低處訪問堆棧,直到返回地址。而 _bnd(X, bnd) 宏就是以 4 字節對齊的變量 X 的大小。
自己實現一個簡單的 printf 函數
#include #include #include #include void print(char *fmt, ...){va_list ptr;va_list temp_ptr = NULL; ? ?/* 用于存儲最終轉換的結果 */char buf[100] = {0}; ? ?/* 用于存儲臨時轉換的結果 */char temp_buf[50] = {0}; ? ?unsigned char index = 0; ? ?unsigned char len, arg_len;len = strlen(fmt); ? ?/* 得到可變參數的首地址 */va_start(ptr, fmt); ? ?for(; *fmt; fmt++){ ? ? ? ?if(*fmt == '%'){ ? ? ? ? ? ?switch(*++fmt){ ? ? ? ? ? ? ? ?case 'd': ? ? ? ? ? ? ? ?case 'D': ? ? ? ? ? ? ? ? ? ?sprintf(temp_buf, "%d", va_arg(ptr, int));temp_ptr = temp_buf; ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?case 's': ? ? ? ? ? ? ? ?case 'S': ? ? ? ? ? ? ? ? ? ?/* 取出當前變量,并將指針指向下一個可變參數 */temp_ptr = va_arg(ptr, char*); ? ? ? ? ? ? ? ? ? ?break;}arg_len = strlen(temp_ptr); ? ? ? ? ? ?strcat(buf+index, temp_ptr);index += arg_len;}else{buf[index] = *fmt;index++;}} ? ?/* 結束取參 */va_end(ptr); ? ?/* 輸出最終轉換結果 */puts(buf); }int main(){print("My name is %s and my height is %d cm.", "Lance#", 178); ? ?return 0; }程序運行結果:
My name is Lance# and my height is 178 cm.聲明:
本文于網絡整理,版權歸原作者所有,如來源信息有誤或侵犯權益,請聯系我們刪除或授權事宜。
總結
以上是生活随笔為你收集整理的C 语言精髓之变参函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用 iPhone 相机识别植物和花
- 下一篇: 从原理到方法,一文讲清如何应对C语言内存