這題目是筆者在某本「有點年代」,收集「面試書籍」看到,意外面試會有人拿不定引數出來考。
筆者忘了題目原形是什麼,但原題意並沒考到 stdarg.c 裡 macro (含 va_list, va_start, va_arg, va_end) 之拆解 ,只有考應用之概念。
以下題目為改自筆者一段 matrix library 範例。
[ 題目 ]
請問下面這段 code 出了什麼錯?
void matrix_set(double** mat, size_t row, size_t col, ...) { size_t i; va_list p; va_start(p, row * col); for( i = 0 ; i < row ; ++i ) for ( j = 0 ; j < col; ++j) mat[i][j] = va_arg( p, double); va_end(p); } /* caller */ double ** mat = new_mat(2, 2); matrix_set(mat, 2, 2, 1 , 2, 3, 4);
-------------------
先「過目」這四組 macro (看看就好,真有興趣再深入。另聲明,只寫這樣是不具可攜性的 )
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 )
[ 錯誤 1 ] caller 端字面常量
matrix_set 在調用 va_arg 時,是以 double 方式吃入,而 caller 端寫的是 1, 2, 3, 4,資料形態為 int,故從 stack 取出時將發生錯誤。
所以,3.0 和 3.0f 的差異,在這裡也很明顯。(敘述錯誤)
[ 錯誤 2 ] va_start
va_start( p , row * col) ; 這行共犯了二個錯誤。分別是語法與邏輯錯誤。
由於 va_start 本身是 macro ,定義大概為 #define va_start(ap,v)( ap =(va_list)&v + _INTSIZEOF(v)),關鍵之錯誤標在紅色部份。在 macro 展開時,會變成 & ( row * col ); 這是不合法的敘述。
第二個錯誤是邏輯上的錯誤,由於 va_start 第二個參數並不是代表「不定參數裡面共有幾個參數」,而是「從哪一個輸入參數開始」。以這題而言,不定參數是從 col 之後,故應該要改成 va_start ( p , col );
會故意寫成這樣,是因為發現不少其他 blog 對於 va_start 參數有錯誤認知,原因應是,大多都是拿 sum 、average、max / min 等之類簡單範例做說明,但沒再特別註明參數之使用。
另外還有另一問題常被抓出來談
void myprint(const char *fmt, ...) { printf(fmt, ...); }
這寫法可知道意圖,但 compile 不會過。需要的是 vprintf 系列函式完成。
void myprint(const char *fmt, ...) { va_list args; va_start (args, fmt); vprintf (fmt, args); va_end (args); }
推斷 BCB TString / MFC CString 應也是用這作法。
留言列表