- 論壇徽章:
- 13
|
本帖最后由 karma303 于 2017-06-05 23:47 編輯
我之前實(shí)現(xiàn)可變參數(shù)的函數(shù),像void foobar( sdf, ...),都是用很土的方式:
unsigned *parg = &sdf; (或是別的什么類(lèi)型,一般都因地制宜)
然后parg[1], parg[2]...一個(gè)個(gè)來(lái)訪問(wèn)!∫矝](méi)出過(guò)什么差錯(cuò)。
今天終于遭報(bào)應(yīng)了,一個(gè)bug調(diào)了至少有4個(gè)小時(shí)。
簡(jiǎn)單點(diǎn)兒說(shuō),就是foobar的堆棧布局,不再是我想象的那樣經(jīng)典的C堆棧布局:
- |__sdf __|
- |_arg1__|
- |_arg2__|
- |___.____|
復(fù)制代碼
是跟蹤了跟蹤了匯編碼才發(fā)現(xiàn),foobar的調(diào)用代碼,還是經(jīng)典的C參數(shù)入棧的方式,但進(jìn)入foobar之后,gcc做了一些處理,例如把sdf復(fù)制到新的棧幀,等等?傊畬(dǎo)致sdf下方不再是__VA_ARGS__了。導(dǎo)致只能用va_start/va_arg/va_end來(lái)訪問(wèn)。
最后把今天這段出bug的代碼貼上吧(好像沒(méi)必要,不過(guò)還是貼上吧)。
- void __tprobe(struct timeval *base, struct timeval *last, ...){
- long *interval, *gone;
- struct timeval now;
- gettimeofday(&now, 0);
- va_list vp;
- va_start(vp, last);
- interval = va_arg(vp, long *);
- if(interval){
- *interval = (now.tv_sec - last->tv_sec) * 1000000+
- now.tv_usec - last->tv_usec;
- gone = va_arg(vp, long *);
- if(gone){
- *gone = (now.tv_sec - base->tv_sec)*1000000+
- now.tv_usec - base->tv_usec;
- }
- }
- va_end(vp);
- *last = now;
- }
復(fù)制代碼- #include<sys/time.h>
- #define TSTAMP_INIT()\
- struct timeval __tm_base;\
- struct timeval __tm_last;\
- gettimeofday(&__tm_base, 0)
- /* call like this:
- * TSTAMP() just make a timestamp. do nothing else.
- * TSTAMP(interval) make timestamp, and query interval to the latest TSTAMP()
- * TSTAMP(interval, gone) and query how many useconds had gone since TSTAMP_INIT
- *
- * The arguments @interval, @gone should be a (long *)pointer for storing result
- */
- #define TSTAMP(...)\
- __tprobe(&__tm_base, &__tm_last,##__VA_ARGS__ ,0)
復(fù)制代碼
之前一直不愿意用va_arg(),一是學(xué)不會(huì),二是是想鍛煉自己多動(dòng)手。現(xiàn)在好了,終于死心了。
|
|