• 主页
  • 系统维护与使用
  • 应用程序开发
  • 内核源代码
  • 驱动程序开发
  • CPU和硬件区
  • UNIX文化
  • Solaris
  • Power Linux

【散分】va_arg问题,令人郁闷的Gcc&CC&hcc&yacc

brucegong 2004-11-25 05:15:50
va_arg如果取值到了最后,没有值可取的时候会怎么样?
C99上没有规定
VC等编译器会返回一个指向0的指针
linux 的info libc也是这么说的

可是TMD实际上返回的是一个不知所云的东西,还得我1000多行代码要动大手术了


郁闷,散闷鄙视这些鸟人

...全文
170 点赞 收藏 15
写回复
15 条回复
brucegong 2004年12月01日







高兄:
你说的我是跟过内存了的,和你说的一样。这个应该是编译软件的实现的问题了。






回复 点赞
gaoxianfeng 2004年12月01日
倒 没人回复了?
我超三次了

之所以windows下可以 我怀疑是他压栈的时候把后面没用地址里的内容付空(置0)
并且 windows的va_arg取的是地址当中的值,就是空了

所以当你在最后一次va_arg取参数时返回就是空了

你如下代码s=str是强行 从本来是指示参数类型个数的地方开始理解成正式数据,
本来va_list 后 调用 va_arg返回的才是 标准应用下的数据值
for (s = str; s != NULL; s = va_arg (ap, const char *))
{
printf("%s \n",s);
}

return result; //result 未付值

//////////////////////////////////////更改后代码如下, 所加代码为证实猜测的微软函数调用传参时做了处理
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

char * concat (const char *str, ...)
{
va_list ap;
size_t total = 1;
const char *s;
char *result;

memset((char*)(&str)+sizeof(char*)*4, 0, 4);//控制结束,没办法的办法可变 个数就不定, 为了让你能明白地址内的值变化
va_start (ap, str);


printf("\r\n----------------------------------\r\n");
/* Determine how much space we need. */
for (s = str; s != NULL; s = va_arg (ap, const char *))
{
char * a;
memcpy(&a, ap, 4);
printf("%u %u \r\n", (int)*((int*)ap), (int)*((int*)&a));//以下个打印为了让你看清楚地址关系
printf("%u %u \r\n", (int)*((int*)ap), a);

printf("test : %s %s\r\n", a, (char*)*((int*)ap));
printf("%s %u %u %u %u %s\n\r",s,&s,s,ap,&ap,(char*)*((int*)ap-1));//这里ap转成int*
}

printf("va_list end!\r\n");
va_end (ap);

memcpy(&result, ap-4, 4);//这里 ap是 char*
return result;
}
int main()
{
char* str;
str = concat("s1 ","s2 ","s3 ","s4 ");
printf("concat return is %s\r\n", str);
return 0;
}



回复 点赞
brucegong 2004年11月30日






问题是:绝大部分的资料里面都说没参数了会返回一个0回来

当然大家说的“后果不可预知”我也看到了

嗯,过去好几天了,郁闷完了。散分





回复 点赞
gaoxianfeng 2004年11月26日
根据 type不同从&ap + 4开始取不同的长度数据出来

这句中 &ap + 4 应该为 ap 地址 即 &fmt +sizeof(char*)
回复 点赞
gaoxianfeng 2004年11月26日
//此时&ap + sizeof(char*) 就是 &ap + 4 (一般系统下)
应该为 ap 的值 为 &fmt + sizeof(char*) 就是 &fmt + 4 (一般系统下)
回复 点赞
gaoxianfeng 2004年11月26日
!!!!!!
你没有理解
typedef char *va_list;
# define va_start(ap, p) (ap = (char *) (&(p)+1))
# define va_arg(ap, type) ((type *) (ap += sizeof(type)))[-1]
# define va_end(ap)

或者
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 )

va_arg 返回 因你的库定义而有些不同
但返回值是不可能 如你所预料的
所以一般可变参数函数定义char * concat (const char *fmt, ...)

。。。。
va_start(ap, fmt);//fmt限定参数个数
//此时&ap + sizeof(char*) 就是 &ap + 4 (一般系统下)
while(*fmt){//判定参数是否结束
。。
switch(*fmt++)
{。。
va_arg(ap,type) 根据 type不同从&ap + 4开始取不同的长度数据出来,并把ap+=sizeof(type)

有的系统va_arg返回type类型地址指针,有的返回type类型数据值,不尽相同。但不会用他来做是否还有参数标志


va_end 就清空ap 一般 指向NULL
。。。
回复 点赞
pacman2000 2004年11月26日
这是man va_arg里面的原话:
If there is no next argument, or if type is not compatible
with the type of the actual next argument (as promoted
according to the default argument promotions), random
errors will occur.

可能标准里没有规定,但是根据最后是NULL来判断是不好的方法。
回复 点赞
xfzhao_cn 2004年11月26日
我 man 了一下 va_arg 的定义, 它说 va_arg 是一个宏定义,
如果到了最后一个,或者是指定的类型错误,将会产生随机错误。:-)
这也许就是你所出现的问题。
我想,这个可以这么解决,concat 函数这么定义:
char * concat (int count, const char *str, ...);
其中count的意思就是,后面字符串的个数。

因为,你每次调用该函数的时候,应该是知道里面字符串的个数的。
回复 点赞
brucegong 2004年11月26日





我也是好几年经验的程序员了,这个问题我既然说出来就肯定是问题
如果有人编译后可以正常执行而不会发生段错误,请留下你的linux版本号和GCC版本号

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

char * concat (const char *str, ...)
{
va_list ap;
size_t total = 1;
const char *s;
char *result;

va_start (ap, str);

/* Determine how much space we need. */
for (s = str; s != NULL; s = va_arg (ap, const char *))
{
printf("%s \n",s);
}

va_end (ap);

return result;
}
int main()
{
char* str;
str = concat("s1 ","s2 ","s3 ","s4 ");
printf(str);
return 0;
}





回复 点赞
brucegong 2004年11月25日





xfzhao_cn() ( ) 信誉:100

明天去公司了再贴代码

代码是仿照info libc /va_arg写的,而且VC下面全部测试通过了的。要不也不会这么郁闷了





回复 点赞
pacman2000 2004年11月25日
能事先确定传进去的参数个数不?
回复 点赞
xfzhao_cn 2004年11月25日
同情, 不过,你能不能把你的写法,让大家来瞅瞅.:)
回复 点赞
lanting918 2004年11月25日

第一次怎么没发出去
回复 点赞
foreverghost 2004年11月25日
同情你楼主!!
回复 点赞
loveisbug 2004年11月25日
哦?仔细说说?
回复 点赞
发动态
发帖子
Linux/Unix社区
创建于2007-08-27

7079

社区成员

7.3w+

社区内容

Linux/Unix社区 应用程序开发区
社区公告
暂无公告