第13題:此題考查的是C的變長參數;
#include<stdio.h>
#include<stdarg.h>
int ripple ( int , ...);
void main()
{
int num;
num= ripple (3,5,7);
printf(" %d\n" , num);
}
int ripple (int n, ...)
{
int i,j;
int k;
va_list p;
k=0;
j= 1;
va_start(p,n);
for(; j<n; ++j)
{
i= va_arg( p , int);
for(; i; i &= i-1 )
++k;
}
return k;
}
這段程序的輸出是:
(a)7 (b) 6 (c) 5 (d) 3
解答:
在C編譯器通常提供了一系列處理可變參數的宏,以屏蔽不同的硬件平臺造成的差異,增加程序的可移植性。這些宏包括va_start、va_arg和va_end等。
採用ANSI標準形式時,參數個數可變的函數的原型聲明是: typefuncname(type para1, type para2, ...)
這種形式至少需要一個普通的形式參數,後面的省略號不表示省略,而是函數原型的一部分。type是函數返回值和形式參數的類型。
不同的編譯器,對這個可變長參數的實現不一樣,gcc4.x中是內置函數.
關於可變長參數,可參閱
http://www.upsdn.net/html/2004-11/26.html
http://www.upsdn.net/html/2004-11/24.html
程序分析
va_list p; /*定義一個變量,保存函數參數列表 的指針*/
va_start(p , n); /*用va_start宏初始化 變量p,va_start宏的第2個參數n,是一個固定的參數,
必須是我們自己定義的變長函數的最後一個入棧的參數
也就是調用的時候參數列表裏的第1個參數*/
for(; j<n; ++j) /* j從1開始,遍歷所有可變參數*/
{
i= va_arg( p , int); /*va_arg取出當前的參數,並認爲取出的參數是一個整數(int)*/
for(; i; i &=i-1 ) /*判斷取出的i是否爲0*/
++k;/* 如果i不爲0,k自加,
i與i-1進行與邏輯運算,直到i爲0
這是一個技巧,下面會談到它的功能*/
}
當我們調用ripple函數時,傳遞給ripple函數的參數列表的第一個參數n的值是3.
va_start初始化p指向第一個未命名的參數(n是有名字的參數),也就是5 (第一個).
每次對va_arg的調用,都將返回一個參數,並且把p指向下一個參數.【從左往右?】
va_arg用一個類型名來決定返回的參數是何種類型,以及在var_arg的內部實現中決定移動多大的距離纔到達下一個參數
(;i; i&=i-1) k++ /*計算i有多少bit被置1*/
5用二進制表示是(101)2
7用二進制表示(111)3
所以k返回 5 (2+3),
舉個例子,就很好理解:
令i=9 = 1001
i-1= 1000
(i-1)+1 = i
1000 +1 = 1001
因爲i與i-1的最右邊的那位(最低位)肯定是不同,如果i&(i-1),那麼最右邊哪位肯定是0,反之亦然. i & (i-1) 這個運算,在二相補的數字系統中,將會消除最右邊的1位