c語言中可變長度參數使用的注意事項

來源:互聯網
上載者:User

標籤:c語言   可變   長度參數   


C語言中可變長度參數極大地方便了我們編程的同時,也非常容易由於使用不慎導致及其隱形錯誤。以下面的這段函數為例,運行一段時間後會隨機出現段錯誤,而且出錯的位置一直穩定在vsprintf()函數裡面。

……………...

a_listap;

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)

returnNULL;

return__redisBlockForReply(c);

}


它主要調用了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;

}


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


256            /* Set newarg so it can be 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)

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()

Theva_arg() macro expands to an expression that has 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 ap sothat the next call returns the next argument.  The argument typeis a

typename specified so that the type of a pointer to an object that 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.


而ap又是作為指標的指標傳遞進來的,因此上層調用函數裡的可變長度參數ap也會改變,著就導致後面對可變長度參數的使用出現段錯誤。因為前面已經遍曆一遍了,ap到末尾了。

理解了這一點,針對一個函數中要調用多個可變長度的參數的用法,安全的用法就是為每一個被調用的函數單獨分配一個可變長度參數va_list。據此,上面的代碼就應該改寫成這樣:


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);

………


本文出自 “儲存之廚” 部落格,請務必保留此出處http://xiamachao.blog.51cto.com/10580956/1951621

c語言中可變長度參數使用的注意事項

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.