当前位置:首页 » 编程语言 » 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);
}