c语言 宽度参数,c语言中可变长度参数使用的注意事项

C语言中可变长度参数极大地方便了我们编程的同时,也非常容易由于使用不慎导致及其隐蔽的错误。以下面的这段函数为例,运行一段时间后会随机出现段错误,而且出错的位置一直稳定在vsprintf()函数里面。

……………...

a_list

ap;

va_start(ap,

cmd);

……………...

rep

= (redisReply *)redisvCommand(rc, cmd, ap);

vsprintf(str,

cmd, ap);

……………...

va_end(ap);

为了深入探究原因,我们下载了redis源代码和glibc源代码。看redis源代码中 redisvCommand的实现:

void

*redisvCommand(redisContext *c, const char *format, va_list ap) {

if

(redisvAppendCommand(c,format,ap) != REDIS_OK)

return

NULL;

return

__redisBlockForReply(c);

}

它主要调用了redisvAppendCommand:

int

redisvAppendCommand(redisContext *c, const char *format, va_list ap)

{

char

*cmd;

int

len;

len

= redisvFormatCommand(&cmd,format,ap);

if

(len == -1) {

__redisSetError(c,REDIS_ERR_OOM,"Out

of memory");

return

REDIS_ERR;

}

else if (len == -2) {

__redisSetError(c,REDIS_ERR_OTHER,"Invalid

format string");

return

REDIS_ERR;

}

if

(__redisAppendCommand(c,cmd,len) != REDIS_OK) {

free(cmd);

return

REDIS_ERR;

}

free(cmd);

return

REDIS_OK;

}

而redisvAppendCommand()函数中使用了va_arg,比如下面的部分代码:

256

/* Set newarg so it can be checked even if it is not

touched. */

257

newarg = curarg;

258

259

switch(c[1]) {

260

case 's':

261

arg = va_arg(ap,char*);

262

size = strlen(arg);

263

if (size > 0)

264

newarg = sdscatlen(curarg,arg,size);

265

break;

266

case 'b':

267

arg = va_arg(ap,char*);

268

size = va_arg(ap,size_t);

269

if (size > 0)

270

newarg = sdscatlen(curarg,arg,size);

271

break;

272

case '%':

273

newarg = sdscat(curarg,"%");

274

break;

275

default:

乍一看,ap传进去都是形式参数,不会改变,但仔细看va_arg的帮助文档可以看到,其实每次调用va_arg()都会改变ap的值:

va_arg()

The

va_arg() macro expands to an expression that has the type and value

of the next argument in the call.  The argument ap is the  va_list ap

initialized by va_start().  Each call to va_arg() modifies ap so

that the next call returns the next argument. The argument type

is a

type

name specified so that the type of a pointer to an object that has

the specified type can be obtained simply by adding a * to type.

The

first use of the va_arg() macro after that of the va_start() macro

returns the argument after last.  Successive invocations return the

values

of the remaining arguments.

而ap又是作为指针的指针传递进来的,因此上层调用函数里的可变长度参数ap也会改变,着就导致后面对可变长度参数的使用出现段错误。因为前面已经遍历一遍了,ap到末尾了。

理解了这一点,针对一个函数中要调用多个可变长度的参数的用法,安全的用法就是为每一个被调用的函数单独分配一个可变长度参数va_list。据此,上面的代码就应该改写成这样:

a_list

ap;

va_list

aq;

va_start(ap,

cmd);

va_copy(aq,

ap);

………

rep

= (redisReply *)redisvCommand(conn, cmd, ap);

vsprintf(str,

cmd, aq);

va_end(ap);

va_end(aq);

………

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值