當前位置:首頁 » 編程語言 » c語言vastart
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

c語言vastart

發布時間: 2022-01-14 07:43:33

A. c語言中可變參數宏的va_start(ap, v)

我把你的提問分為3個問題:
1、為什麼printf("%s", ap);輸出不了?
2、va_start(ap, v)的定義中為什麼使用二級指針?
3、va_arg(ap,t) 的定義中為什麼用*(t *),它的作用是?
在解釋之前,先確認一個小問題:
在C語言中,指針這種類型的大小實際上一樣的,我的意思是說無論是char *a,還是int *a,或者是char **a,a這個指針變數所佔用的內存空間是一樣的(都是sizeof(a),究竟是等於4,還是8取決於CPU的位數)
先回答第一個問題:
你應該知道va_list的定義:typedef char * va_list;
也就是說ap可以理解為一個char *類型的變數,va_start(ap,c)這個執行之後,ap確實指向了可變參數列表中的第一個參數,注意【是ap這個指針指向了第一個參數】,而如果你的第一個參數是一個字元串(C語言中也就意味著是一個char*的變數),這樣的話,ap這個指針就指向了一個char*類型的指針變數,【指向指針的指針變數是二級指針變數】這個我就不用多說了吧,所以printf("%s", ap);是無法輸出的,而修改為printf("%s", *(char **)ap);應該就可以輸出了!

然後是第二個問題:
這里先說一下函數調用過程中參數傳遞的問題:
【 函數參數是以數據結構:棧的形式存取,從右至左入棧。
首先是參數的內存存放格式:參數存放在內存的堆棧段中,在執行函數的時候,從最後一個開始入棧。因此棧底高地址,棧頂低地址,舉個例子如下:
void func(int x, char *y, char z);
那麼,調用函數的時候,實參 char z 先進棧,然後是 char *y,最後是 int x,因此在內存中變數的存放次序是 x->y->z,因此,從理論上說,我們只要探測到任意一個變數的地址,並且知道其他變數的類型,通過指針移位運算,則總可以順藤摸瓜找到其他的輸入變數。】
注意,x,y,z這幾個變數是存放到堆棧中的,所以我們需要獲得的不是y這個變數本身,而是它在堆棧中的地址,而ap這個指針變數就是保存著堆棧中函數入參的地址的,所以在va_start(ap, v)的定義中要使用&v,而不管v變數本身是什麼類型的(哪怕v是一個指針變數,甚至是二級指針)&v都表示一個地址,所以可以強制轉換為va_list類型(也就是char *)。

第三個問題:
要睡覺了,先自己想吧,如果還不明白,就留言追問吧。

B. C++ 宏定義 _crt_va_start 是什麼功能

這個是c++可變參函數 定義 參數首地址的宏

C. va_start的代碼說明

void arg_test(int i, ...);
int main(int argc,char *argv[])
{
int int_size = _INTSIZEOF(int);
printf(int_size=%d , int_size);
arg_test(0, 4);
//arg_cnt(4,1,2,3,4);
return 0;
}
void arg_test(int i, ...)
{
int j=0;
va_list arg_ptr;
va_start(arg_ptr, i);
printf(&i = %p , &i);//列印參數i在堆棧中的地址
printf(arg_ptr = %p , arg_ptr);//列印va_start之後arg_ptr地址
/*這時arg_ptr應該比參數i的地址高sizeof(int)個位元組,即指向下一個參數的地址*/
j=*((int *)arg_ptr);
printf(%d %d , i, j);
j=va_arg(arg_ptr, int);
printf(arg_ptr = %p , arg_ptr);//列印va_arg後arg_ptr的地址
/*這時arg_ptr應該比參數i的地址高sizeof(int)個位元組,即指向下一個參數的地址,如果已經是最後一個參數,arg_ptr會為NULL*/
va_end(arg_ptr);
printf(%d %d , i, j);
}
說明:
int int_size = _INTSIZEOF(int);得到int類型所佔位元組數
va_start(arg_ptr, i); 得到第一個可變參數地址
根據定義(va_list)&v得到起始參數的地址, 再加上_INTSIZEOF(v) ,就是其實參數下一個參數的地址,即第一個可變參數地址.
j=va_arg(arg_ptr, int); 得到第一個可變參數的值,並且arg_ptr指針上移一個_INTSIZEOF(int),即指向下一個可變參數的地址.
va_end(arg_ptr);置空arg_ptr,即arg_ptr=(void *)0;

D. C語言 函數變參數的問題(va_list,va_start,va_arg,va_end)之提問

好像少東西了

E. C語言的變參技術,va_start,va_arg,va_end這幾個函數怎麼用

#include <stdarg.h> // 必須包含的頭文件

int Add(int start,...) // ...是作為佔位符
{
va_list arg_ptr; // 定義變參起始指針
int sum=0; // 定義變參的和
int nArgValue =start; //

va_start(arg_ptr,start); // arg_ptr指向第一個變參
do
{
sum+=nArgValue; // 求和
nArgValue = va_arg(arg_ptr,int); // arg_ptr指向下一個變參
}
while(nArgValue != 0); // 判斷結束條件;結束條件是自定義為=0時結束

va_end(arg_ptr); // 復位指針
return sum;
}

函數的調用方法為Add(1,2,3,0);這樣,必須以0結尾,因為變參函數結束的判斷條件就是讀到0停止。

解釋:

所使用到的宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );

typedef char * va_list;
#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 )

1、首先把va_list被定義成char*,這是因為在我們目前所用的PC機上,字元指針類型可以用來存儲內存單元地址。而在有的機器上va_list是被定義成void*的

2、定義_INTSIZEOF(n)主要是為了某些需要內存的對齊的系統.這個宏的目的是為了得到最後一個固定參數的實際內存大小。在我的機器上直接用sizeof運算符來代替,對程序的運行結構也沒有影響。(後文將看到我自己的實現)。

3、va_start的定義為 &v+_INTSIZEOF(v) ,這里&v是最後一個固定參數的起始地址,再加上其實際佔用大小後,就得到了第一個可變參數的起始內存地址。所以我們運行va_start(ap, v)以後,ap指向第一個可變參數在的內存地址,有了這個地址,以後的事情就簡單了。

這里要知道兩個事情:
⑴在intel+windows的機器上,函數棧的方向是向下的,棧頂指針的內存地址低於棧底指針,所以先進棧的數據是存放在內存的高地址處。
(2)在VC等絕大多數C編譯器中,默認情況下,參數進棧的順序是由右向左的,因此,參數進棧以後的內存模型如下圖所示:最後一個固定參數的地址位於第一個可變參數之下,並且是連續存儲的。
|--------------------------|
| 最後一個可變參數 | ->高內存地址處
|--------------------------|
|--------------------------|
| 第N個可變參數 | ->va_arg(arg_ptr,int)後arg_ptr所指的地方,
| | 即第N個可變參數的地址。
|--------------- |
|--------------------------|
| 第一個可變參數 | ->va_start(arg_ptr,start)後arg_ptr所指的地方
| | 即第一個可變參數的地址
|--------------- |
|------------------------ --|
| |
| 最後一個固定參數 | -> start的起始地址
|-------------- -| .................
|-------------------------- |
| |
|--------------- | -> 低內存地址處

(4) va_arg():有了va_start的良好基礎,我們取得了第一個可變參數的地址,在va_arg()里的任務就是根據指定的參數類型取得本參數的值,並且把指針調到下一個參數的起始地址。
因此,現在再來看va_arg()的實現就應該心中有數了:
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
這個宏做了兩個事情,
①用用戶輸入的類型名對參數地址進行強制類型轉換,得到用戶所需要的值
②計算出本參數的實際大小,將指針調到本參數的結尾,也就是下一個參數的首地址,以便後續處理。

(5)va_end宏的解釋:x86平台定義為ap=(char*)0;使ap不再 指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不會為va_end產生代碼,例如gcc在linux的x86平台就是這樣定義的. 在這里大家要注意一個問題:由於參數的地址用於va_start宏,所以參數不能聲明為寄存器變數或作為函數或數組類型. 關於va_start, va_arg, va_end的描述就是這些了,我們要注意的 是不同的操作系統和硬體平台的定義有些不同,但原理卻是相似的.

F. 代碼「va_start(ap,fmt)」是什麼意思

VA_LIST 是在C語言中解決變參問題的一組宏,在<stdarg.h>頭文件下。

G. C語言 函數變參數的問題(va_list,va_start,va_arg,va_end)

void showname (int num,...);//第一個參數最好存放參數個數
showname (2,locitem1,locitem2);//調用
void showname (int num,...)
{
va_list ap;
Item loc_item;
va_start (ap, num);
for(;num;num--){
loc_item = va_arg(ap,Item);
printf("%s is %s\n",loc_item.name,loc_item.kind);
}
va_end (ap);
}

va_start (ap, num);通過判斷num的長度來確定 變長參數列表裡第一個參數的地址
所以它判斷的不是num的值而是num所佔內存的大小

H. 這里的va_start以及va_arg函數都是什麼意思呀之前學的清華大學出版社的c語言,好像沒看

這個是宏吧,讀取變參用的,應該是頭文件「stdarg.h」中的
這個博客講的很清楚,建議樓主看一下~
http://justsee.iteye.com/blog/1637173

I. c 語言訪問可變參數 va_start 宏中的第二個參數有什麼意義

然後用va_start宏初始化變數arg_ptr,這個宏的第二個參數是第一個可變參數的前一個參數,是一個固定的參數.


如果前一個參數是NULL,那麼它就是NULL。




simple_va_fun(inti,...)
{
va_listarg_ptr;
intj=0;

va_start(arg_ptr,i);==>i如果為NULL,這里是不可能,但是對於別的函數,有可能為NULL,那麼就是NULL
j=va_arg(arg_ptr,int);
va_end(arg_ptr);
printf("%d%d ",i,j);
return;
}

J. C語言中標識符__VA_ARGS__是什麼的縮寫

//
這個是函數可變參數的用法
#include
"stdio.h"
#include
"stdarg.h"
int
max_list(int
n,...)
{
va_list
var_arg;
//
建立參數列表
int
w,i,max=0;
va_start(var_arg,n);
//
設置變數n為起始參數
for(i=0;
i
max)
max
=
w;
if(w
<
0)
va_end(var_arg);
//
當w<0時結束列表,不能再添加參數
}
return
max;
}
void
main(void)
{
int
p,s;
s
=
5;
//
添加5個整形數作為參數
p
=
max_list(s,4,85,2,4,7,6);
//
6是多出的參數,忽略不計,得結果85
printf("%d\n",p);
}