《C語言介面與實現》實驗——可變參數表的使用(va_list, va_start, va_arg, va_end)

來源:互聯網
上載者:User

《C語言介面與實現》作為介面庫,源檔案中大量使用了可變參數表,這些到底是怎麼使用的?先來看這幾個例子,基本明白了可變參數表使用。後面部分從網上整理了原理:

來源程式:

#include <stdio.h>#include <stdarg.h>#include <string.h>//// 使用樣本1:追加串// void Va_Fn1(char *dest, char *data, ...){va_list ap;char *p = data;//指向第一個可變參數//第二個參數就是寫 ... 前面那個va_start(ap, data);//遍曆每一個可變參數,取出來使用while(1){//訪問當前這個可變參數,先用,後遍曆!!strcat(dest, p);p = va_arg(ap, char *);if (p == NULL) break;}//結束va_end(ap);}//// 使用樣本2:累加和// int Va_Fn2(int a, ...){int ret = a;//指向第一個參數int sum = 0;va_list ap;//第二個參數就是寫 ... 前面那個va_start(ap, a);//遍曆每一個可變參數,取出來使用while(1){//先用,後遍曆sum += ret;ret =va_arg(ap, int);if (ret == -1) break;}//結束va_end(ap);return sum;}//// 使用樣本3:使用資料結構// typedef struct{int x;int y;}MY_TYPE;void Va_Fn3(int n, MY_TYPE *p, ...){int i = 0;va_list ap;MY_TYPE *tmp = p;//第二個參數就是寫 ... 前面那個va_start(ap, p);for(; i<n; i++){printf("\t%d-----%d\n", tmp->x, tmp->y);tmp = va_arg(ap, MY_TYPE *);}//結束va_end(ap);}//// 使用樣本4:稍複雜的可變參數表//(char *, int, int),(char *, int, int), ......// void Va_Fn4(char *msg, ...){va_list ap;int i, j;char *str = msg;//指向第一個參數//第二個參數就是寫 ... 前面那個va_start(ap, msg);while(1){//使用可變參數表,先使用i = va_arg(ap, int);j = va_arg(ap, int);printf("\t%s---%d----%d\n", str, i, j);//後遍曆str = va_arg(ap, char *);//第二個參數是【可變參數】的類型if (str == NULL) break;}//結束va_end(ap);}void main(){//樣本1char dest[1000] = {0};Va_Fn1(dest, "Hello ", "OK ", "歡迎 ", "Yes ", NULL);printf("樣本1 = %s\n", dest);//樣本2int x = Va_Fn2(9, 9, 1, 3, 90, -1);printf("樣本2 = %d\n", x);//樣本3MY_TYPE a, b, c;a.x = 100;a.y = 300;b.x = 1100;b.y = 1300;c.x = 6100;c.y = 6300;printf("樣本3:\n");Va_Fn3(3, &a, &b, &c);//樣本4printf("樣本4:\n");Va_Fn4("Hello", 1, 2, "XYZ", 300, 600, "ABC", 77, 88, NULL);}

輸出:

樣本1 = Hello OK 歡迎 Yes樣本2 = 112樣本3:        100-----300        1100-----1300        6100-----6300樣本4:        Hello---1----2        XYZ---300----600        ABC---77----88Press any key to continue

原理:

1. 函數參數是以資料結構:棧的形式存取,從右至左入棧

2. 首先是參數的記憶體存放格式:參數存放在記憶體的堆棧段中,在執行函數的時候,從最後一個開始入棧。因此棧底高地址,棧頂低地址,舉個例子如下:

void func(int x, float y, charz);
那麼,調用函數的時候,實參char z 先進棧,然後是 float y,最後是 intx,因此在記憶體中變數的存放次序是 x->y->z,因此,從理論上說,我們只要探測到任意一個變數的地址,並且知道其他變數的類型,通過指標移位元運算,則總可以順藤摸瓜找到其他的輸入變數。<----這就是原理!!

3. 看源碼(vc98/include/stdarg.h):(注意是X86相關的,不是mips,不是ALPHA的,不是PPC等等的!)

typedef char *  va_list;

#ifdef  _M_IX86

#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 )

#elif   defined(_M_MRX000)

這裡有複雜的宏,展開它:在“工程屬性” —〉“C/C++”—〉“Project Options” 手工填入/P,然後rebuild,會產生於.cpp同名的.i檔案,裡面的宏被展開了。來看展開後的第一個函數:

void Va_Fn1(char *dest, char *data, ...){va_list ap;char *p = data;( ap = (va_list)&data + ( (sizeof(data) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) );while(1){strcat(dest, p);p = ( *(char * *)((ap += ( (sizeof(char *) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) - ( (sizeof(char *) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) );if (p == 0) break;}( ap = (va_list)0 );}

再看展開的第二個函數,這個較簡單:

int Va_Fn2(int a, ...){int ret = a;int sum = 0;va_list ap;( ap = (va_list)&a + ( (sizeof(a) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) );while(1){sum += ret;ret =( *(int *)((ap += ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) - ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) );if (ret == -1) break;}( ap = (va_list)0 );return sum;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.