簡體   English   中英

用於 const 雙指針的慣用 C

[英]idiomatic C for const double-pointers

我知道在 C 中,您不能將例如char**隱式轉換為const char** (參見C-FaqSO question 1SO Question 2 )。

另一方面,如果我看到一個 function 聲明如下:

void foo(char** ppData);

我必須假設 function 可能會更改傳入的數據。因此,如果我正在編寫不會更改數據的 function,我認為最好聲明:

void foo(const char** ppData);

甚至:

void foo(const char * const * ppData);

但這會讓 function 的用戶陷入尷尬的 position 中。 他們可能有:

int main(int argc, char** argv)
{
    foo(argv); // Oh no, compiler error (or warning)
    ...
}

為了干凈地調用我的 function,他們需要插入一個演員表。

我主要來自 C++ 背景,由於 C++ 更深入的 const 規則,這不是一個問題。

C 中的慣用解決方案是什么?

  1. 將 foo 聲明為采用char** ,並僅記錄它不會更改其輸入的事實? 這似乎有點惡心,尤其是。 因為它會懲罰可能擁有想要傳遞它的const char**的用戶(現在他們必須拋棄const-ness)

  2. 強制用戶轉換他們的輸入,添加 const-ness。

  3. 還有什么?

雖然你已經接受了答案,但我想找到3)即宏。 你可以用你的函數用戶只寫一個調用foo(x);的方式來編寫它們foo(x); 其中x可以是const -qualified或不是。 這個想法將有一個宏CASTIT執行轉換並檢查參數是否是有效類型,另一個是用戶界面:

void totoFunc(char const*const* x);    
#define CASTIT(T, X) (                 \
   (void)sizeof((T const*){ (X)[0] }), \
   (T const*const*)(X)                 \
)
#define toto(X) totoFunc(CASTIT(char, X))

int main(void) {
   char      *     * a0 = 0;
   char const*     * b0 = 0;
   char      *const* c0 = 0;
   char const*const* d0 = 0;
   int       *     * a1 = 0;
   int  const*     * b1 = 0;
   int       *const* c1 = 0;
   int  const*const* d1 = 0;

   toto(a0);
   toto(b0);
   toto(c0);
   toto(d0);
   toto(a1); // warning: initialization from incompatible pointer type
   toto(b1); // warning: initialization from incompatible pointer type
   toto(c1); // warning: initialization from incompatible pointer type
   toto(d1); // warning: initialization from incompatible pointer type
}

CASTIT宏看起來有點復雜,但它只是首先檢查X[0]是否與char const*兼容。 它使用復合文字。 然后將其隱藏在sizeof以確保實際上永遠不會創建復合文字,並且X不會被該測試評估。

然后是一個普通的演員,但這本身就太危險了。

正如您在main示例中所看到的,這可以准確地檢測出錯誤的情況。

宏可以實現很多這樣的東西。 我最近const限定數組編寫了一個復雜的例子。

2比1好.1雖然很常見,因為大量的C代碼根本不使用const。 因此,如果您正在為新系統編寫新代碼,請使用2.如果您正在為const很少的現有系統編寫維護代碼,請使用1。

使用選項2.選項1具有您提到的缺點,並且類型安全性較低。

如果我看到一個帶有char **參數的函數,並且我有一個char *const *或類似的char *const * ,我會復制並傳遞它,以防萬一。

現代(C11+)方式使用_Generic來保持類型安全和 function 指針:

// joins an array of words into a new string;
// mutates neither *words nor **words
char *join_words (const char *const words[])
{
// ...
}

#define join_words(words) join_words(_Generic((words),\
          char ** : (const char *const *)(words),\
    char *const * : (const char *const *)(words),\
          default : (words)\
))

// usage :
int main (void)
{
    const char *const words_1[] = {"foo", "bar", NULL};
    char *const words_2[] =       {"foo", "bar", NULL};
    const char *words_3[] =       {"foo", "bar", NULL};
    char *words_4[] =             {"foo", "bar", NULL};

// none of the calls generate warnings:
    join_words(words_1);
    join_words(words_2);
    join_words(words_3);
    join_words(words_4);

// type-checking is preserved:
    const int *const numbers[] = { (int[]){1, 2}, (int[]){3, 4}, NULL };
    join_words(numbers);
// warning: incompatible pointer types passing
// 'const int *const [2]' to parameter of type 'const char *const *'

// since the macro is defined after the function's declaration and has the same name,
// we can also get a pointer to the function
    char *(*funcptr) (const char *const *) = join_words;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM