這題目是筆者在某本「有點年代」,收集「面試書籍」看到,意外面試會有人拿不定引數出來考。

筆者忘了題目原形是什麼,但原題意並沒考到 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 應也是用這作法。

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 edisonx 的頭像
    edisonx

    Edison.X. Blog

    edisonx 發表在 痞客邦 留言(2) 人氣()