我們知道,數組作為函數參數時退化為一個指針,再加上一個數組長度的參數,便可以處理數組:
void(int n, int arr[]);
其實這就是告訴編譯器,以arr為基準地址,處理n個int類型的數據。
我們也知道,對于C函數,函數參數的壓棧操作是從右向左進行的(字長内存對齊),從最後一個參數開始壓棧,直到将第一個參數壓棧為止。
由此,如果有一個基準地址及數據長度,似乎可以對不确定函數參數個數的函數進行處理了。
在C中,正是以符号“…”作為占位符來進行變參(函數參數數量可變)處理的。
#include <stdio.h>
void print(int n, ...)
{
int *p,i;
p = &n 1;
for (i=0;i<n;i )
printf("%d\t",p[i]);
printf("\n");
return ;
}
int main()
{
print(4,12,34,56,78);
return 0;
}
從上面的代碼可以看到,首先在print()函數中使用了占位符“…”,因此該函數在編譯的時候被當成變參函數來處理,對該函數調用中的參數一一 進行壓棧處理。如下圖所示為函數參數在棧中的存儲結構。上述代碼定義了一個int型指針變最P,由于函數參數的壓棧順序是從右向左,由髙地址到低地址,所以在函數中通過“p=&n 1;”得到的是第一個可變參數的地址,接下來通過一個for循環一一取出函數中的參數。在此使用第一個函數參數表示傳遞可變參數的個數。在使用變參函數的時候,必須知道參數什麼時候結束。如果沒有給出變參函數的個數,直接給出第一個參數,那麼必須約定一個參數作為結束标志。
在stdarg.h中,通過宏來實現變參函數的泛型化:
#include <stdio.h>
#include <stdarg.h>
void print(int n,...)
{
int arg,i;
va_list p;
va_start(p,n);
for(i=0;i<n;i )
{
arg = va_arg(p,int);
printf("%d\t",arg);
}
printf("\n");
va_end(p);
return ;
}
int main()
{
print(3,21,32,54);
return 0;
}
宏定義如下:
typedef char * va_list;
#define _INTSIZEOF(n) ( (sizeof(n) sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap = _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
宏va_start(ap,v)可以取得第二個參數的地址,存儲到char *類型的ap。
宏_INTSIZEOF(v)用于于内存對齊(編譯器都有默認的内存對齊,通常是一個字長)。
宏va_arg(ap,t)用于實現ap的的類型轉換,并通過 =實現ap的副作用,指向下一下參數的地址。
宏va_end(ap)用于将指針ap置0。
~(sizeof(int) - 1)的值對于32位系統是-4,二進制為:
1 1111111 11111111 11111111 11111100
宏_INTSIZEOF(v)的表達式能夠實現類型v按4個字節(32位系統)進行類型對齊(編譯器會默認按一個字長來對齊内存,int一般也是定義為一個字長的長度)。
以下實例是實現n個int類型做函數參數的實例:
#include <stdio.h>
typedef char * va_list;
#define va_start(ap,v) ( ap = (va_list)&v sizeof(v) )
#define va_arg(ap,t) ( *(t *)((ap = sizeof(t)) - sizeof(t)) )
#define va_end(ap) ( ap = (va_list)0 )
void print(int n,...)
{
int arg,i;
va_list p;
va_start(p,n);
for(i=0;i<n;i )
{
arg = va_arg(p,int);
printf("%d\t",arg);
}
printf("\n");
va_end(p);
return ;
}
int main()
{
print(4,12,34,56,78);
return 0;
}
因為int類型的長度與編譯器默認内存對齊的長度一緻,所以不需要内存對齊的宏_INTSIZEOF(v)。
如果實現n個字符類型的函數參數,就需要添加内存對齊的宏了:
#include <stdio.h>
typedef char * va_list;
#define va_start(ap,v) ( ap = (va_list)&v sizeof(v) )
#define va_arg(ap,t) ( *(t *)((ap = sizeof(t)) - sizeof(t)) )
#define va_end(ap) ( ap = (va_list)0 )
void print(int n,...)
{
int arg,i;
va_list p;
va_start(p,n);
for(i=0;i<n;i )
{
arg = va_arg(p,char);
printf("%c\t",arg);
}
printf("\n");
va_end(p);
return ;
}
int main()
{
print(4,'A','B','C','D');
return 0;
}
如果是不同類型的n個函數參數呢?則需要逐一分析類型來實現數據解析和地址的偏轉了:
#include <stdio.h>
#include <stdlib.h>
typedef char * va_list;
#define _INTSIZEOF(n) ( (sizeof(n) sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap = _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
void myprintf(char* fmt, ...)
{
va_list p;
char c;
va_start(p,fmt);
do
{
c =*fmt;
if (c != '%')
{
putchar(c);
}
else
{
switch(* fmt)
{
case 'd':
printf("%d",*((int*)p));
break;
case 'c':
printf("%c",*((int*)p));
break;
case 'f':
printf("%3.2f",*((double*)p));
va_arg(p,int);
default:
break;
}
va_arg(p,int);
}
fmt;
}while (*fmt != '\0');
va_end(p);
return;
}
void main()
{
int a = 12;
short b = 56;
char c = 'A';
double f = 123.2;
myprintf("a = %d\t b = %d\t c = %c\t f = %f\n",a,b,c,f);
return ;
}
-End-
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!