众所周知,在宏中使用typeof
使它们与类型无关,例如container_of()
以及Linux内核中的许多其他宏。毫无疑问,typeof
关键字在这些宏中使用时释放了大量功能。
此问题是关于typeof
关键字的进一步使用。除了Macros之外,关键字还可以为C代码带来哪些其他背景?
答案 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
。 但我错了:它没有被错误命名,因为它也可以添加 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_signed
和std::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字节的数组,反之亦然。但您无法使用它将指针转换为另一个指针,以避免严格的别名规则。