2014-04-04 23 views
3

衆所周知,在宏中使用typeof可以使它們與類型無關,如container_of()和Linux內核中的許多其他宏。 typeof關鍵字在這些宏中使用時會釋放出很多能量,這是毫無疑問的。C中的typeof用法,除宏之外

這個問題是關於進一步使用typeof關鍵字。除了宏之外,關鍵字還有哪些其他的上下文可以爲C代碼帶來很多好處?

+7

'typeof'是非標準的;它似乎是一個gcc特定的擴展。 –

+0

老實說,想不出任何。除了宏之外,所有東西都是強類型的,所以你已經知道你正在處理的任何東西的類型。我想你已經發現了他們的有用性的專屬領域。 – enhzflep

+0

C語言中的類型沒有那麼多東西 - 畢竟,語言是相當簡單的。然而,C++是完全不同的問題,並且編寫了大量精美的代碼來依賴'typeof'和更近期的'decltype'語句。 – oakad

回答

1

typeof的一個用途是const-cast一個2維數組。在GCC,構建體:

extern void foo(const int a[2][2]); // or equivalently a[][2] 
    int a[2][2]; 
    foo(a); 

將生成:

「警告:使從兼容的指針類型 '富' 的參數1」。

(見http://c-faq.com/ansi/constmismatch.html的原因),以解決這個問題的方法之一是使用大錘般的演員,如:

foo((void *)a); 

這樣的廣播將樂意接受任何你,也許是錯誤地給了它。

但我們可以變得更加微妙。通過使用下面的代碼示例中給出的鑄造宏CONST_CAST_2D,警告被消除。更重要的是,如果您嘗試將其應用於2-D數組以外的任何其他位置,您將收到編譯器錯誤/警告。對於指針指針,CONST_CAST_PP的工作方式類似。

#define CONST_CAST_2D(x) ((const typeof((x)[0][0])(*)[countof((x)[0])])(x)) 
#define CONST_CAST_PP(x) ((const typeof(**(x))**)(x)) 
#define countof(x)  (sizeof(x)/sizeof 0[x]) // semi-standard define 

static void foo(const int a[][2]) {} // takes const 
static void bar(const int **b) {} // takes const 

int main(void) { 
    int a[2][2];   // non-const 
    int **b;    // non-const 
    foo(CONST_CAST_2D(a)); // ok 
    bar(CONST_CAST_PP(b)); // ok 
    return 0; 
} 

CONST_CAST_PP提供了一個乾淨和可靠的解決方案,以一個經常被問道的問題,例如:

而且CONST_CAST_2D解析:

2

typeof第二使用是生成指針常量,或指針函數返回值,如顯示在下面的例子:

#include <stdio.h> 
#include <time.h> 
#include <sys/socket.h> 

#define AMPERSAND(x) (&(typeof(x)){x}) 

int main(void) { 
    printf("%s\n", ctime(AMPERSAND(time(0)))); // pointer to time_t 
    setsockopt(0, SOL_SOCKET, SO_REUSEADDR, AMPERSAND(1), sizeof 1); 
    return 0; 
} 

這允許直接的功能組合物,而不必保存在臨時對象命名變量。 (不幸的是,這並沒有擴展到g ++。)

1

有些人(包括我自己)不喜歡C++ const_cast<>運算符的語法,因爲;

  • 它似乎名不副實,因爲它刪除const
  • 它似乎違反DRY,因爲它需要一個冗餘類型arg。

但我錯了:它不是名不副實,因爲它也可以添加const和/或volatile「CV」限定詞,它只是部分違反了幹,因爲編譯器將捕獲的任何錯誤。所以我不喜歡它,並使用它:它比C風格演員更安全。

使用GCC的typeof,你可以有幾乎相同類型安全C.

下面的C代碼示例給出了一個CONST_CAST(T, x)宏,並說明其使用方法:

#define REMOVE_QUALIFIER(cv, T, x) /* this macro evaluates its args only once */ \ 
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), cv T), ((T)(x)), \ 
    (void)0) 
#define ADD_QUALIFIER(cv, T, x) /* this macro evaluates its args only once */  \ 
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), T), ((cv T)(x)), \ 
    (void)0) 
#ifdef __GNUC__ 
#define CONST_CAST(T, x) REMOVE_QUALIFIER(const, T, x) // "misnamed" 
#else 
#define CONST_CAST(T, x) ((T)(x)) // fallback to standard C cast 
#endif 

void foo(void); 
void foo(void) { 
    const int *a = 0; 
    const float *x = 0; 
    int *b  = a;       // warning 
    int *c  = (int *)a;     // no warning, unsafe standard cast 
    int *d  = (int *)x;     // no warning, and likely wrong 
    int *e  = CONST_CAST(int *, a);  // ok 
    int *f  = CONST_CAST(int *, x);  // error 
    unsigned *g = CONST_CAST(unsigned *, a); // error 
    const int **h = &b;       // warning 
    const int **i = ADD_QUALIFIER(const, int **, &b); // ok 
    const int **j = ADD_QUALIFIER(const, int **, &x); // error 
} 

這種技術還可以用於改變類型的符號性,讓人想起C++的std::make_signedstd::make_unsigned或Boost特徵。例如:

#define MAKE_UNSIGNED(T, x) ADD_QUALIFIER(unsigned, T, x) // T usually char* 
0

這種使用GCC的typeof的是另一個重釋投,利用工會雙關語。

它可以應用於標量和結構,以及指針。它只給出一個R值。

#ifdef __GNUC__ 
#define PUN_CAST(T, x) (((union {typeof(x) src; T dst;})(x)).dst) 
#else 
#define PUN_CAST(T, x) (*(T*)&(x)) //<-- classic pun: breaks strict aliasing rules 
#endif 

警告:您可以使用它將指針轉換爲4或8字節的數組,或反之。但是你不能用它把指針轉換成另一個指針,以避免嚴格的別名規則。