The variable length parameter in C language greatly facilitates our programming, and it is also very easy to use inadvertently resulting in its hidden errors. Taking this function as an example, a segment error occurs randomly after a period of time, and the position of the error is stable in the vsprintf () function.
..................
A_listap;
Va_start (Ap,cmd);
..................
rep= (redisreply *) Redisvcommand (RC, CMD, AP);
VSPRINTF (Str,cmd, AP);
..................
Va_end (AP);
To delve into why, we downloaded the Redis source code and the glibc source code. See the implementation of Redisvcommand in the Redis source code:
Void*redisvcommand (Rediscontext *c, const char *format, va_list AP) {
if (Redisvappendcommand (c,format,ap)! = REDIS_OK)
Returnnull;
Return__redisblockforreply (c);
}
It mainly calls the Redisvappendcommand:
Intredisvappendcommand (Rediscontext *c, const char *format, va_list AP) {
Char*cmd;
Intlen;
len= Redisvformatcommand (&CMD,FORMAT,AP);
if (len = =-1) {
__redisseterror (C,redis_err_oom, "outof memory");
Returnredis_err;
}else if (len = =-2) {
__redisseterror (C,redis_err_other, "Invalidformat string");
Returnredis_err;
}
if (__redisappendcommand (c,cmd,len)! = REDIS_OK) {
Free (cmd);
Returnredis_err;
}
Free (cmd);
RETURNREDIS_OK;
}
In the Redisvappendcommand () function, Va_arg is used, such as the following part of the code:
The Newarg so it can checked even if it is nottouched. */
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)
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)
Newarg = Sdscatlen (curarg,arg,size);
271 break;
272 case '% ':
273 Newarg = Sdscat (curarg, "%");
274 break;
275 Default:
At first glance, the AP passes in the form parameter, will not change, but look closely va_arg's help document can see, in fact each call Va_arg () will change the value of the AP:
Va_arg ()
Theva_arg () macro expands to an expression of the type and valueof the next argument in the call. The argument AP is the va_list ap initialized by Va_start (). Each call to Va_arg () modifies APS sothat the next call returns the next argument. The argument Typeis a
TypeName specified So, the type of a pointer to an object, hasthe specified type can be obtained simply by adding A * to type.
Thefirst use of the va_arg () macro after that of the Va_start () macroreturns the argument after last. Successive invocations return the
Valuesof the remaining arguments.
The AP is also passed in as a pointer pointer, so the variable-length parameter AP in the upper-call function changes, causing a segment error in the use of the variable-length parameter after it is encountered. Because the front has been traversed again, the AP to the end.
With this in mind, for a function to invoke the use of multiple variable-length parameters, a safe use is to assign a variable-length parameter va_list to each of the called functions individually. Accordingly, the above code should be rewritten like this:
A_listap;
Va_listaq;
Va_start (Ap,cmd);
Va_copy (AQ,AP);
.........
rep= (redisreply *) Redisvcommand (conn, CMD, AP);
vsprintf (Str,cmd, aq);
Va_end (AP);
Va_end (aq);
.........
This article is from the "Store Chef" blog, so be sure to keep this source http://xiamachao.blog.51cto.com/10580956/1951621
Considerations for using variable length parameters in C language