可变形参函数的整形提升问题

测试昵称666 2010-12-10 09:06:06
看到资料上这么说:
在va_arg中获取float和double使用的Type都是double,而获取char、short和int使用的Type都是int。

我自己测试了一下:

#include <iostream>
#include <stdarg.h>
using namespace std;

void my_printf(char *fmt, ...)
{
va_list ap;
char *p, *sval;
int ival;
double dval;
char cval;
va_start(ap, fmt);
for(p=fmt; *p; ++p)
{
if(*p != '%')
putchar(*p);
else
switch(*++p)
{
case 'd':
ival = va_arg(ap, int);
printf("%d", ival);
break;
case 'f':
dval = va_arg(ap, double);
printf("%f", dval);
break;
case 's':
sval = va_arg(ap, char*);
for(; *sval; ++sval)
putchar(*sval);
break;
case 'c':
cval = va_arg(ap, char);
printf("%c", cval);
break;
default:
putchar(*p);
break;

}
}
va_end(ap);
}


int _tmain(int argc, _TCHAR* argv[])
{
int a = 5;
char b = 'm';
my_printf("%d %c", a, b); //依然输出5 m
return 0;
}


程序依然输出5 m,
我像了解下,这里具体是如何提升的?有何影响?为何我这里没变化?
...全文
188 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 we_sky2008 的回复:]

引用 18 楼 wtq493841534 的回复:
引用 17 楼 we_sky2008 的回复:

引用 16 楼 wtq493841534 的回复:
引用 15 楼 we_sky2008 的回复:

楼主啊,你怎么老是提到va_start呢?
va_start只是用来初始化typedef char * va_list; 中的va_list指针的

取参数时就和va_star……
[/Quote]

嗯,有机会多交流交流。
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 wtq493841534 的回复:]
引用 17 楼 we_sky2008 的回复:

引用 16 楼 wtq493841534 的回复:
引用 15 楼 we_sky2008 的回复:

楼主啊,你怎么老是提到va_start呢?
va_start只是用来初始化typedef char * va_list; 中的va_list指针的

取参数时就和va_start没有任何关系了


啊!懂了!兄弟,你太给力了!……
[/Quote]
一起学习
呵呵
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 we_sky2008 的回复:]

引用 16 楼 wtq493841534 的回复:
引用 15 楼 we_sky2008 的回复:

楼主啊,你怎么老是提到va_start呢?
va_start只是用来初始化typedef char * va_list; 中的va_list指针的

取参数时就和va_start没有任何关系了


啊!懂了!兄弟,你太给力了!真是很感谢你!

那个va_arg取参数时,会把……
[/Quote]

呵呵,真的很感谢你!
咱们讨论了这么久,中间居然还没有其他朋友回答。。。。

问题圆满解决。结贴。
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 wtq493841534 的回复:]
引用 15 楼 we_sky2008 的回复:

楼主啊,你怎么老是提到va_start呢?
va_start只是用来初始化typedef char * va_list; 中的va_list指针的

取参数时就和va_start没有任何关系了


啊!懂了!兄弟,你太给力了!真是很感谢你!

那个va_arg取参数时,会把char 提升为int,所以在你给出的程序中,中间三个就……
[/Quote]
你终于明白了,我很欣慰
哈哈
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 we_sky2008 的回复:]

楼主啊,你怎么老是提到va_start呢?
va_start只是用来初始化typedef char * va_list; 中的va_list指针的

取参数时就和va_start没有任何关系了
[/Quote]

啊!懂了!兄弟,你太给力了!真是很感谢你!

那个va_arg取参数时,会把char 提升为int,所以在你给出的程序中,中间三个就没有,如果在char时改成arg+=sizeof(int),那么输出就会正常,对吧?
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
楼主啊,你怎么老是提到va_start呢?
va_start只是用来初始化typedef char * va_list; 中的va_list指针的

取参数时就和va_start没有任何关系了
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 we_sky2008 的回复:]

引用 10 楼 wtq493841534 的回复:
引用 8 楼 we_sky2008 的回复:

引用 5 楼 wtq493841534 的回复:
引用 2 楼 we_sky2008 的回复:

引用 1 楼 we_sky2008 的回复:
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由……
[/Quote]

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 
#define va_arg(ap,t) \
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

似乎这里,va_arg和va_start都是向上提升?
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 we_sky2008 的回复:]
引用 10 楼 wtq493841534 的回复:
引用 8 楼 we_sky2008 的回复:

引用 5 楼 wtq493841534 的回复:
引用 2 楼 we_sky2008 的回复:

引用 1 楼 we_sky2008 的回复:
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于……
[/Quote]
因为可变参数函数在传递参数时有类型提升,所以编译器在取完参数之后使用_INTSIZEOF宏
目的就是保证指针移动是正确的,对于这个函数当然前提是前面的fmt格式要和后面传递的参数一致,
若不一致也可能会出现错误,
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 wtq493841534 的回复:]
引用 8 楼 we_sky2008 的回复:

引用 5 楼 wtq493841534 的回复:
引用 2 楼 we_sky2008 的回复:

引用 1 楼 we_sky2008 的回复:
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的i……
[/Quote]
对于va_start宏,只是用来指向第一个可变参数的,其实就是定义一个指针啊
而va_arg则是先获得当前参数值,然后往前移动指针指向下一个参数的
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
另外,你不是想验证你的疑惑吗?
可以这么着来验证,自己实现可变参数函数,
我在注释的地方故意将取完char参数之后将指针移动1个字节,这样就会出现错误了:

#include <iostream>

using namespace std;

void my_printf(char *fmt, ...)
{
char *arg = (char *)(&fmt + 1);
char *p, *sval;
int ival;
double dval;
char cval;

for(p=fmt; *p; ++p)
{
if(*p != '%')
putchar(*p);
else
{
switch(*++p)
{
case 'd':
ival = *(int *)arg;
arg += sizeof(int);
printf("%d", ival);
break;
case 'f':
dval = *(double *)arg;
arg += sizeof(double);
printf("%f", dval);
break;
case 's':
sval = *(char **)arg;
arg += sizeof(char **);
for(; *sval; ++sval)
putchar(*sval);
break;
case 'c':
cval = *(char *)arg;
arg += sizeof(char);//这里因为参数传递时有类型提升,所以此处应该是arg += sizeof(char);,移动一个字节的话就会有问题
printf("%c", cval);
break;
default:
putchar(*p);
break;
}
}
}
}


int main()
{
int a = 5;
char b = 'm';
my_printf("%d %c %c %c %c %c", a, b, b, b, b, b); //这里就会有问题了


system("pause");
return 0;
}




测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 we_sky2008 的回复:]

引用 5 楼 wtq493841534 的回复:
引用 2 楼 we_sky2008 的回复:

引用 1 楼 we_sky2008 的回复:
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度

C/C++ code

ty……
[/Quote]

汗,罪过,这里没看清楚。

不过似乎va_start和va_args都是相加?
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 wtq493841534 的回复:]
引用 6 楼 we_sky2008 的回复:

引用 4 楼 wtq493841534 的回复:
引用 1 楼 we_sky2008 的回复:

楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度
C/C++ code
楼主要是看懂这个代码的话就懂了
呵呵

#define va_arg(ap,t) \
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )



typ……
[/Quote]
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 wtq493841534 的回复:]
引用 2 楼 we_sky2008 的回复:

引用 1 楼 we_sky2008 的回复:
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度

C/C++ code

typedef char * va_list;
#def……
[/Quote]
汗,
因为是宏,所以它实现为先+=偏移量,注意是+=, 值已经被加过了
然后在用加过的值减去加上的值就是未加前的值嘛
就像:
int i = 0;
(i += 4) - 4;//这个表达式的结果为0,但执行完之后i的值为4了
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 we_sky2008 的回复:]

引用 4 楼 wtq493841534 的回复:
引用 1 楼 we_sky2008 的回复:

楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度
C/C++ code

typedef char * va_list;
#de……
[/Quote]

请问他是如何移动4个字节的?
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 wtq493841534 的回复:]
引用 1 楼 we_sky2008 的回复:

楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度
C/C++ code

typedef char * va_list;
#define _INTSIZEOF(n) \
((siz……
[/Quote]

#define va_arg(ap,t) \
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

这里尽管t的类型是char, 但是取出参数之后,指针是移动了4个字节的
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 we_sky2008 的回复:]

引用 1 楼 we_sky2008 的回复:
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度

C/C++ code

typedef char * va_list;
#define _INTSIZEOF(n) \
((si……
[/Quote]

我怎么感觉取出时,(ap += _INTSIZEOF(t)) - _INTSIZEOF(t))
相互抵消了?
测试昵称666 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 we_sky2008 的回复:]

楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度
C/C++ code

typedef char * va_list;
#define _INTSIZEOF(n) \
((sizeof(n)+sizeof(int)-1)&……
[/Quote]

我感觉不管他是int还是char,在用%c输出时,都是按字符输出的。

你的意思是,他在va_start时,提升为int,而且va_args时,没有变化?
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
楼主要是想验证你的想法的话
可以不使用<stdarg.h>中的宏定义,自定义可变参数函数
在传递参数时传递两个char, 在取参数时取一个参数移动一个char的大小问题就会出现了
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 we_sky2008 的回复:]
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度

C/C++ code

typedef char * va_list;
#define _INTSIZEOF(n) \
((sizeof(n)+sizeof(int)-1)&……
[/Quote]
从这几个宏定义可以看出,入参数栈和从参数栈取参数时都是有类型提升的
所以也就不会出现问题了
we_sky2008 2010-12-10
  • 打赏
  • 举报
回复
楼主看看下面的宏定义就知道为什么没出现问题了,
传递参数时,char被提升为int, 但是在从参数栈里取参数时,尽管楼主写成这样子,但是由于使用了_INTSIZEOF宏,实际上取参数时还是取的int的长度

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 )

64,282

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧