日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深度探索va_start、va_arg、va_end

發布時間:2025/3/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深度探索va_start、va_arg、va_end 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?采用C語言編程的時候,函數中形式參數的數目通常是確定的,在調用時要依次給出與形式參數對應的所有實際參數。但在某些情況下希望函數的參數個數可以根據需要確定。典型的例子有大家熟悉的函數printf()、scanf()和系統調用execl()等。那么它們是怎樣實現的呢?

? ? C編譯器通常提供了一系列處理這種情況的宏,以屏蔽不同的硬件平臺造成的差異,增加程序的可移植性。這些宏包括va_start、va_arg和va_end等。在講解以上宏之前我們先了解一下調用函數時傳入參數的處理過程。

一、函數傳入參數過程

? ? 一個函數包括函數名、傳入參數、返回參數以及函數體,函數體編譯后的二進制代碼儲存在程序代碼區。當用戶調用某個函數時,系統會通過函數名(C++中會涉及到mangled命名處理)查找函數體入口指針并壓入棧中,之后將傳入參數以從右至左的順序壓入棧中(棧空間是往低地址方向增長的,也即棧底對應高地址,棧頂對應低地址),示例如下:

#include <iostream>
using?namespace?std;

void?fun(int?a, ...)
{
? ?int?*temp = &a;
? ? temp++;
? ?for?(int?i =?0; i < a; ++i) { cout<< *temp <<?endl;
? ? ? temp++;
? ? }
}

int?main()
{
? ?int?a =?1;
? ?int?b =?2;
? ?int?c =?3;
? ?int?d =?4;
? ? fun(4, a, b, c, d);

? ? system("pause");
? ?return?0;
}
//?Output:
//?1
//?2
//?3
//?4

二、va_start、va_arg和va_end宏定義

? 接著我們再來看看va_start、va_arg和va_end等宏的具體定義。

#define?_INTSIZEOF(n)? ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )?//?此句宏的作用是將類型n的大小向上取成4的倍數,如n為char型的話結果即為4

#ifdef? __cplusplus
#define?_ADDRESSOF(v)? ( &reinterpret_cast<const char &>(v) )?//?vs2015中此句高亮,作用是將v的地址重新解釋成char*型
#else
#define?_ADDRESSOF(v)? ( &(v) )
#endif

#define?_crt_va_start(ap,v)? ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define?_crt_va_arg(ap,t)? ? ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define?_crt_va_end(ap)? ? ? ( ap = (va_list)0 )

#ifndef _VA_LIST_DEFINED
#ifdef _M_CEE_PURE
typedef System::ArgIterator va_list;
#else
typedefchar?*? va_list;? ? ??//?vs2015中此句高亮
#endif?/* _M_CEE_PURE */
#define?_VA_LIST_DEFINED
#endif
//?以上宏定義出現在vadef.h,通過stdio.h即可使用

#define va_start _crt_va_start #define va_arg _crt_va_arg #define va_end _crt_va_end // 以上宏定義出現在stdarg.h中,若要使用則需加上 #include <stdarg.h>

三、va_start、va_arg和va_end使用示例

? ? 容易看出,以上宏定義主要涉及地址操作,va_start獲取第二個傳入參數的地址給va_list類型變量(假設為arg_ptr),va_arg用于獲取當前參數值并將指針arg_ptr往后移,va_end則是將arg_ptr置為空。va_start、va_arg、va_end具體用法示例如下:

#include <stdio.h>
#include<stdarg.h>

int?fun(int?x,?int?y) {
? ?return?x -?y;
}

int?fun(int?count, ...) {
? ? va_list arg_ptr;? ? ??//?等同于 char *arg_ptr;
? ??int?nArgValue =?count;
? ?int?nArgCout =?0;?
? ? va_start(arg_ptr, count);?//?使arg_ptr指向第二個參數的地址

? ? printf("The 1 th arg: %d\n", nArgValue);? ??//?輸出第一個參數的值
? ??int?sum =?0;
? ?for?(int?i =?0; i < count; i++)
? ? {
? ? ? ?++nArgCout;
? ? ? ? nArgValue= va_arg(arg_ptr,?int);//?將arg_ptr所指參數返回成int并移動arg_ptr使其指向后一個參數,這里假設傳入參數均是int型
? ? ? ? printf("The %d th arg: %d\n", i+2, nArgValue);??//?輸出各參數的值
? ? ? ? sum +=?nArgValue;
? ? }

? ? va_end(arg_ptr);? ?//?將arg_ptr置為空
? ??return?sum;
}

int?main() {
? ? cout<< fun(0) <<?endl;
? ? cout<< fun(1,?1) << endl;??//?優先匹配到函數int fun(int, int), 輸出0
? ? cout << fun(2,?3,?4) <<?endl;
? ?system("pause");



? ? return?0;

}

// Output: // 0 // 0 // The 1 th arg: 2 // The 2 th arg: 3 // The 3 th arg: 4 // 7

? ? 注意:以上宏操作并不提供參數個數獲取操作,這需要用戶在函數中獲取,如第二個fun函數使用count指明個數,printf通過解析第一個傳入參數來確定參數個數與類型等。

總結

以上是生活随笔為你收集整理的深度探索va_start、va_arg、va_end的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。