C語言實現可變參函數

今天給大家介紹如何用C語言實現可變參數函數。

在日常的C語言編程中,相信很多人都喜歡用printf函數,不知道你是否有關注過該函數的聲明呢?

<code>int printf(const char *format, ...);/<code>

這裡的...並非碼哥偷懶寫的省略符,而是一種特殊符號,或者說助記符,或者佔位符,隨你們喜歡啦。它的含義是表示在format參數後有不定個數不定類型的參數。

例如:

<code>printf("%s %d %c\\n", "abc", 10, 'A');/<code>

在本例中:

  • format是"%s %d %c\\n"
  • 其後不定參數部分有三個參數,分別為字符數組類型、整型、字符型。



實現可變參數函數

下面切入正題,碼哥將實現一個可變參函數,直接上代碼吧。

<code>/*
* Author:碼哥比特
*/
#include <stdarg.h>
#include <unistd.h>
#include <string.h>


void my_print(char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
while (*fmt) {
if (*fmt != '%') {
write(STDOUT_FILENO, fmt, 1);
++fmt;
continue;
}
++fmt;
switch (*fmt) {
case 's': {
char *s = va_arg(arg, char *);
write(STDOUT_FILENO, s, strlen(s));
break;
}
default:
write(STDOUT_FILENO, fmt, 1);
break;
}
++fmt;
}
va_end(arg);
}

int main(void)
{
my_print("%s %s %s\\n", "abc", "bcd", "cde");
return 0;
}/<string.h>/<unistd.h>/<stdarg.h>/<code>

這段函數中,碼哥實現了一個名為my_print的函數,這是一個可變參函數。

簡短起見,我僅實現了%s輸出字符串的功能,其餘%x都會將x原封不動輸出。

實現需要的工具函數

可變參的實現需要使用stdarg.h頭文件下聲明的函數和數據類型來完成。在

本例中涉及1個數據類型和3個函數,他們分別是:

  • va_list類型
  • void va_start(va_list ap, last);
  • type va_arg(va_list ap, type);
  • void va_end(va_list ap);

在使用可變參部分前,首先要定義一個va_list變量,後續處理需要用到這個變量,大家也可以粗略的將其看作一個句柄(誰說只有fd能叫句柄呢~)。

下面我們對三個函數分別進行說明:

va_start

這個函數有兩個參數,第一個參數是我們定義的va_list變量,第二個參數是可變參...左側最靠近...的變量名,本例為fmt。通過這個函數就可以定位到可變參部分的位置了。

這裡額外多說一點,在IA-32架構下,C調用約定中,函數參數都是由右向左依此壓入棧中的。因此在獲取可變參時,有一種比較hack的實現方式是,獲取到可變參...前的那個參數在內存中的地址作為指針類型,然後自增獲取其後的可變參數。(注意,x86架構處理器,棧增長方向是由高向低)。但是由於64位系統下,C函數參數為了性能提升,有部分是存放在寄存器中,因此這種方式不具有可移植性。

va_arg

這個函數(暫且當函數看吧)用於獲取每一個可變參數,由其聲明可以看到,返回值為我們需要的類型,函數參數為va_list變量和我們期望的函數類型。

可以參考上面的例子中,我們獲取一個char *參數的寫法:

<code>char *s = va_arg(arg, char *);/<code>

va_end

用於銷燬va_list結構所使用的資源。


到此,一個可變參函數就實現了,感興趣的讀者可以自己嘗試實現int、float、char等其他類型參數。


分享到:


相關文章: