今天自己看了一下不定參數的文章,又深入理解了一下,總結如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
typedef char * va_list;
#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 )
第一眼看到_INTSIZEOF這個很不理解,不知道什麼意思。
內事不懂問百度,於是搜了一下:
http://topic.csdn.net/u/20080719/10/824a4013-7a42-443c-abb6-dea0e0aa638e.html
現摘錄關鍵的:
1:我們知道對於IX86,sizeof(int)一定是4的整數倍,所以~(sizeof(int) - 1) )的值一定是
右面[sizeof(n)-1]/2位為0,整個這個宏也就是保證了右面[sizeof(n)-1]/2位為0,其餘位置為1
2:目的在於把sizeof(n)的結果變成至少是sizeof(int)的整倍數,這個一般用來在結構中實現按int的倍數對齊。
如果sizeof(int)是4,那麼,當sizeof(n)的結果在1~4之間是,_INTSIZEOF(n)的結果會是4;當sizeof(n)的結果在5~8時,
_INTSIZEOF(n)的結果會是8;當sizeof(n)的結果在9~12時,_INTSIZEOF(n)的結果會是12;……總之,會是sizeof(int)的倍數。
剩下的一個自訂類型和三個宏也解釋一下:
typedef char * va_list;
//va_list的類型就是char *,記憶體操作的基本單位是一個位元組,下面將用 va_list來進行位元組的操作
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//將ap指標移動到參數v後面一個參數的開始位置
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//將ap指標移動到t型別參數後面一個參數的開始位置,返回t型別參數的值
#define va_end(ap) ( ap = (va_list)0 )
//將ap指標清空
以char *make_message(const char *fmt, ...);
make_message("Hello world %d %s", 5, endmsg)為例說明如下:
1:ap指標的操作是在函數的參數棧上操作的
2:可以通過&fmt獲得函數參數棧第一個參數的地址
3:參數棧上儲存參數時,如果sizeof(type)小於四,就是是四個位元組的,大於四的是四的倍數,例如long long 是8
char類型,short類型,long類型,還有指標類型,將這些類型代到_INTSIZEOF宏中結果為4,long long類型結果為8
4:對於字串類型,函數參數棧上儲存的是指向字串地址的指標,不是具體的字串
將make_message函數參數棧列印出來(12個位元組),和fmt的指標內容以及endmsg指標的內容比較可以看出來
基本就是這些,下面是測試程式,歡迎大家討論,指正。
ps:上一篇中(不定參數),舉的例子都是約定好了不定參數的類型的,在擷取參數的時候,直接指定這個類型。
但是如果不知道不定參數的類型如何處理呢?就像printf一樣。
#include <stdio.h><br />#include <string.h><br />#include <stdlib.h><br />#include <stdarg.h></p><p>#define MAXOUT_PERLEN 16<br />#define MAX_LEN 5<br />#define PREFIX_LEN 13</p><p>//#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))</p><p>char *make_message(const char *fmt, ...);<br />void DumpBinary(void *data, int len);</p><p>int main()<br />{<br /> char c;<br /> int i;<br /> short s;<br /> char *name = "yournamei";<br /> long l;<br /> long long ll;<br /> char *msg = NULL;<br /> char *endmsg = "change yourself!";</p><p> printf("char %d/n", _INTSIZEOF(char));<br /> printf("char * %d/n", _INTSIZEOF(char *));<br /> printf("int %d/n", _INTSIZEOF(int));<br /> printf("short %d/n", _INTSIZEOF(short));<br /> printf("name %d/n", _INTSIZEOF(name));<br /> printf("long %d/n", _INTSIZEOF(long));<br /> printf("long long %d/n", _INTSIZEOF(long long));</p><p> printf("pointer of endmsg content is 0x%x/n", endmsg);</p><p> msg = make_message("Hello world %d %s", 5, endmsg);<br /> if(msg == NULL)<br /> printf("Msg Make Error!");<br /> else<br /> {<br /> printf("%s", msg);<br /> free(msg);<br /> }</p><p> return 0;<br />}</p><p>void dumpParamStack(const char **fmt, int len)<br />{<br /> printf("fmt pointer content is 0x%x/n", *fmt);<br /> DumpBinary((void *)fmt, len);<br />}</p><p>char *<br />make_message(const char *fmt, ...)<br />{<br /> /* Guess we need no more than 100 bytes. */<br /> int n, size = 100;<br /> char *p, *np;<br /> va_list ap;</p><p> if ((p = malloc(size)) == NULL)<br /> return NULL;</p><p> printf("_INTSIZEOF fmt = %d/n", _INTSIZEOF(fmt));</p><p> while (1) {<br /> /* Try to print in the allocated space. */<br /> va_start(ap, fmt);<br /> n = vsnprintf(p, size, fmt, ap);<br /> va_end(ap);<br /> /* If that worked, return the string. */<br /> if (n > -1 && n < size)<br /> {<br /> dumpParamStack((const char *)&fmt, 12);<br /> return p;<br /> }<br /> /* Else try again with more space. */<br /> if (n > -1) /* glibc 2.1 */<br /> size = n+1; /* precisely what is needed */<br /> else /* glibc 2.0 */<br /> size *= 2; /* twice the old size */<br /> if ((np = realloc (p, size)) == NULL) {<br /> free(p);<br /> return NULL;<br /> } else {<br /> p = np;<br /> }<br /> }<br />}</p><p>void DumpBinary(void *data, int len)<br />{<br /> int i = 0, j = 0;<br /> int line = 0, remain = 0;<br /> unsigned char lineBuffer[PREFIX_LEN + MAX_LEN * MAXOUT_PERLEN + 1] = {0};</p><p> line = len / MAXOUT_PERLEN;<br /> remain = len % MAXOUT_PERLEN;</p><p> for(i = 0; i < line; i++)<br /> {<br /> sprintf(lineBuffer, "0x%08x : ", i);<br /> for(j = 0; j < MAXOUT_PERLEN; j++)<br /> {<br /> sprintf(lineBuffer + PREFIX_LEN + j * MAX_LEN, "0x%02x ", *((unsigned char *)data + i * MAXOUT_PERLEN + j));<br /> }<br /> //cout << lineBuffer << endl;<br /> printf("%s/n", lineBuffer);</p><p> memset(lineBuffer, 0, sizeof(lineBuffer));<br /> }</p><p> if(remain != 0)<br /> sprintf(lineBuffer, "0x%08x : ", i);</p><p> for(j = 0; j < remain; j++)<br /> {<br /> sprintf(lineBuffer + PREFIX_LEN + j * MAX_LEN, "0x%02x ", *((unsigned char *)data + i * MAXOUT_PERLEN + j));<br /> }<br /> //cout << lineBuffer << endl;<br /> printf("%s/n", lineBuffer);<br />}</p><p>
下面是輸出:
char 4
char * 4
int 4
short 4
name 4
long 4
long long 8
pointer of endmsg content is 0x4030aa
_INTSIZEOF fmt = 4
fmt pointer content is 0x403127
0x00000000 : 0x27 0x31 0x40 0x00 0x05 0x00 0x00 0x00 0xaa 0x30 0x40 0x00
Hello world 5 change yourself!