C语言可变参数,va_arg、va_list,va_start,va_end,_INTSIZEOF浅析

学习C语言可变参数时,发现

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

乍一看,完全不明白该宏的作用是啥,仔细分析后发现,该宏是求类型n是整型int的多少倍(向上取整).

在32位win中,sizeof(int)=4 。

如果n为char,一字节,则sizeof(n)=  (1+3)&~3=4;

若n为short,sizeof(n)=(2+3)&~3=4;

若n为double,sizeof(n)=(8+3)&~3=8;

该宏的效果等价于:

#define _MY_INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) / (sizeof(int)) * (sizeof(int)) )

基本思想是:让类型n的长度加上整型长度-1,为了保证与整型长度相除向上取到整数倍,再乘以整型长度,这样就得出

以整型来存储该类型需要多少字节,目的就是为了传可变个数参数时,内存对齐。

/ (sizeof(int)) * (sizeof(int)) )  可以优化的地方在于,int为2的n次方个字,比如16位的win中,int为了2Byte(2^0),

32位的win中,整型长度为4(2^2)。以二进制的观点,除以2^n相当于右移两n位,乘以2^n相当于左移n位。

则/ (sizeof(int)) * (sizeof(int)) )  相当于右移n位再左移n位,也就可以说让它低n位都置0即可。

& ~(sizeof(int) - 1)  就可以达到该效果,以sizeof(int)=4为例,~(sizeof(int)-1)=~3=(1111 1111 1111 1100)b

该数再与前面的数进行按位与操作,可将低2位置0。

想到这一步,不得不佩服写出这句程序的人高明之处!

 

接着看va_list,它是定义成char*,因为c/cpp没有byte类型。

va_start(ap,v); 运行这句后,ap指向第一个参数的地址。调试时发现sum(num,...)几个参数都是顺序存储,

ap= v的地址加上v所占的字段数,故ap指向num后的第一个参数。

再看va_arg,#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

这一句也比较复杂,思考了许久,开始想不明白为什么要加_INTSIZEOF(t),又减_INTSIZEOF(t),它又是如何递增

指向下一个参数。原来它是先将 ap+=_INTSIZEOF(t),修 改ap的值,此时ap指向下一个参数,再减去

_INTSIZEOF(t),又得到原来的地址,将其返回,转化为类型*的指针,再取指针所指地址的值。这也是处很巧妙的地方。

最后va_end( argptr );将指针argptr置为0;

结语:

MSVC中,可变参数实现方法是将,传过去的值或指针封装成以int为基本单位的数组结构中,再用一个指针去遍历。

仍有一点迷惑的地方是,各种调用约定,如何影响参数传递。换不同的调用约定__cdel,__stdcall发现内存中参数顺序一样。

 


#include <stdio.h>

#include <stdarg.h>



int sum( int num, ... ) {

	int answer = 0;

	/*

		typedef char *  va_list;

	*/

	va_list argptr;

	/*

		#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

		#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

	*/

	va_start( argptr, num );

	

	for( ; num > 0; num-- ) {

		//#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

        answer += va_arg( argptr, int );

	}

	//#define va_end(ap)      ( ap = (va_list)0 )

	va_end( argptr );

	

	return( answer );

}





void my_printf( char *format, ... ) {

	va_list argptr;

	

	va_start( argptr, format );

	

	while( *format != '\0' ) {

		// string

		if( *format == 's' ) {

			char* s = va_arg( argptr, char * );

			printf( "Printing a string: %s\n", s );

		}

		// character

		else if( *format == 'c' ) {

			char c = (char) va_arg( argptr, int );

			printf( "Printing a character: %c\n", c );

			break;

		}

		// integer

		else if( *format == 'd' ) {

			int d = va_arg( argptr, int );

			printf( "Printing an integer: %d\n", d );

		}

		

		format++;

	}

	

	va_end( argptr );

}



#pragma pack(push, 1)

struct PackedStructure

{	

	char a[3];

	int b;

	short c;

};

#pragma pack(pop)		//sizeof(PackedStructure) : 9







#define _MY_INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) / (sizeof(int)) * (sizeof(int)) ) 





int main(){



	printf("sizeof(char*):%d\n",sizeof(char*));



	int answer = sum( 4, 4, 3, 2, 1 );

	printf( "The answer is %d\n", answer );



	my_printf( "sdc", "This is a string", 29, 'X' );



	printf("\nsizeof(int):%d\n",sizeof(int));



	printf("sizeof(PackedStructure):%d\n",sizeof(PackedStructure));



	printf("_INTSIZEOF(PackedStructure):%d\n",_INTSIZEOF(PackedStructure));

		

	printf("_INTSIEEOF(char)",_INTSIZEOF(char));



	printf("\nsizeof(double):%d\n",sizeof(double));



	printf("_INTSIZEOF(double):%d\n",_INTSIZEOF(double));

	

	printf("_MY_INTSIZEOF(double):%d\n",_MY_INTSIZEOF(double));

	

	return 0;

}

 

你可能感兴趣的:(sizeof)