代碼:
-(NSString*)makeDrink:(NSString*)drink Fruit:(NSString*)fruit Food:(NSString*)foodOne,...;
{
NSMutableArray* arr = [[NSMutableArrayalloc]init];
va_listparams;
idargument;
if(foodOne) {
va_start(params, foodOne);
while((argument =va_arg(params,id))) {
[arraddObject:argument];
}
va_end(params);
}
returnnil;
}
------------------------------------------------------------
用法:
Person *man = [Person alloc] init];
[man makeDrink:@"beer" Fruit:@"banana" Food:@"rice",@"beef",@"fish",@"potato",@"vegetables",nil];
------------------------------------------------------------
知識點:
va_list/va_start/va_arg/va_end這幾個宏,都是用于函數的可變參數的。
我們來看看在vs2008中,它們是怎么定義的:
1:///stdarg.h
2:#defineva_start _crt_va_start
3:#defineva_arg _crt_va_arg
4:#defineva_end _crt_va_end
5:
6:///vadefs.h
7:#define_ADDRESSOF(v)? ( &reinterpret_cast(v) )
8:? typedefchar*? va_list;
9:#define_INTSIZEOF(n)? ( (sizeof(n) +sizeof(int) - 1) & ~(sizeof(int) - 1) )
10:#define_crt_va_start(ap,v)? ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
11:#define_crt_va_arg(ap,t)? ? ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
12:#define_crt_va_end(ap)? ? ? ( ap = (va_list)0 )
再看看各個宏的功能是什么?
va_list用于聲明一個變量,我們知道函數的可變參數列表其實就是一個字符串,所以va_list才被聲明為字符型指針,這個類型用于聲明一個指向參數列表的字符型指針變量,例如:va_list ap;//ap:arguement pointer
va_start(ap,v),它的第一個參數是指向可變參數字符串的變量,第二個參數是可變參數函數的第一個參數,通常用于指定可變參數列表中參數的個數。
va_arg(ap,t),它的第一個參數指向可變參數字符串的變量,第二個參數是可變參數的類型。
va_end(ap) 用于將存放可變參數字符串的變量清空(賦值為NULL).
va_start的功能是要把,ap指針指向可變參數的第一個參數位置處,
#define_crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
先取第一個參數的地址,在sum函數中就是取number的地址并且將其轉化為char *的(因為char *的指針進行加減運算后,偏移的字節數才與加的數字相同, 如果為int *p,那么p+1實際上將p移動了4個字節),然后加上4(__INITSIZEOF(number)=(4+3)&~3),這樣就將ap指向了可變參數字符串的第一個參數。
#define_INTSIZEOF(n) ( (sizeof(n) +sizeof(int) - 1) & ~(sizeof(int) - 1) )
以int所占的字節為標準進行對其操作。
如果int占四字節,則以四字節對齊為標準讀取數據。
va_arg是要從ap中取下一個參數。
#define_crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
對于這個宏,哥糾結了很久,最后終于搞清楚了,究其原因就是自己C語言功底不扎實,具體表現在沒有搞清楚賦值表達式的值是怎么運作的。
我們看這個宏,首先是ap = ap + __INTSIZEOF(t)。注意到,此時ap已經被改變了,它已經指向了下一個參數,我們令x=ap + __INTSIZEOF(t);
那么括號內就變成了(x – __INTSIZEOF(t)),但是這里沒有賦值運算符,所以ap的值沒有發生變化,此時ap仍然指向的是當前參數的下一個參數的位置,
也就是說ap指向的位置比當前正在處理的位置超前了一個位置。
其實寫成下面的形式就簡單明了了:
#define?? va_arg(ap,t)?? (*(t?? *)((ap?? +=?? _INTSIZEOF(t)),ap?? -?? _INTSIZEOF(t)))
分析:為什么要將ap指向當前處理參數的下一個參數了?
經過上面的分析,我們知道va_start(ap,v)已經將ap指向了可變參數列表的第一個參數了,以后我們每一步操作都需要將ap移動到下一個
參數的位置,由于我們每次使用可變參數的順序是:va_start(ap,v)—>va_arg(ap,t);這樣我們在第一次去參數的時候,其實ap已經指向了
第二個參數開始的位置,所以我們用表達式的方式獲得一個指向第一個參數的臨時指針,這樣我們就可以采用這種一致的方式來處理可變參數列表。
(感覺沒表達的十分清楚,希望各位朋友糾正~~~~~~)。
下圖是我的例子程序中去參數的情況(時間倉促,畫得很丑,請原諒):
va_end(ap)? 將聲明的ap指針置為空,因為指針使用后最后設置為空。
參考資料:
http://topic.csdn.net/u/20110830/15/a3630fc4-3c5f-4a1e-bbee-949ba7b4cbe0.html
http://topic.csdn.net/u/20070120/12/e8b7363b-6404-4d91-9307-01e5ed996f3d.html