除了宏之外,在C中使用typeof

时间:2014-04-04 00:34:44

标签: c linux-kernel

众所周知,在宏中使用typeof使它们与类型无关,例如container_of()以及Linux内核中的许多其他宏。毫无疑问,typeof关键字在这些宏中使用时释放了大量功能。

此问题是关于typeof关键字的进一步使用。除了Macros之外,关键字还可以为C代码带来哪些其他背景?

4 个答案:

答案 0 :(得分: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 :(得分:1)

typeof的一个用途是 const-cast 一个二维数组。在gcc中,构造:

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

将生成:

  

&#34;警告:传递&#39; foo&#39;的参数1来自不兼容的指针类型&#34;。

(请参阅http://c-faq.com/ansi/constmismatch.html了解原因。)解决此问题的一种方法是使用类似大锤的演员表,例如:

  foo((void *)a);

这样的演员很乐意接受任何你,也许是错误的,给予它。

但我们可以更加细腻。通过使用以下代码示例中给出的cast-macro CONST_CAST_2D,可以消除警告。更重要的是,如果您尝试将其应用于除二维数组之外的任何其他内容,您将收到编译器错误/警告。对于指向指针的指针,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 :(得分:1)

有些人(包括我自己)不喜欢C ++ const_cast<>运算符的语法,因为;

  • 似乎名不副实,因为删除了 const
  • 它似乎违反了DRY,因为它需要冗余类型arg。

但我错了:它没有被错误命名,因为它也可以添加 const和/或volatile&#34; cv&#34;限定符,它只是部分违反DRY,因为编译器会捕获任何错误。所以我不喜欢它并使用它:它比C风格的演员更安全。

使用gcc&#39; 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*

答案 3 :(得分:0)

gcc&#39; typeof的这种使用是另一种重新解释,使用了union-punning。

它可以应用于标量和结构,也可以应用于指针。它只给出一个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字节的数组,反之亦然。但您无法使用它将指针转换为另一个指针,以避免严格的别名规则。