最近偶然又看到了typeof这个东西,突然想不起咋回事了,不知道是不是老年痴呆的前兆。。。。废话不说了,来看一下。
首先typeof这个东西并不是ISO/IEC 9899:1999里的,也就是说不是标准C的运算符,这是gcc的一个扩展。在gcc的官方文档中单独列了一章来说这个东西(5.6 Referring to a Type with typeof) 。具体的内容如下:
Another way to refer to the type of an expression is with typeof. The syntax of using of
this keyword looks like sizeof, but the construct acts semantically like a type name defined
with typedef.
There are two ways of writing the argument to typeof: with an expression or with a
type. Here is an example with an expression:
typeof (x[0](1))
This assumes that x is an array of pointers to functions; the type described is that of the
values of the functions.
Here is an example with a typename as the argument::
typeof (int *)
Here the type described is that of pointers to int.
If you are writing a header file that must work when included in ISO C programs, write
__typeof__ instead of typeof. See Section 5.39 [Alternate Keywords], page 239.
A typeof-construct can be used anywhere a typedef name could be used. For example,
you can use it in a declaration, in a cast, or inside of sizeof or typeof.
typeof is often useful in conjunction with the statements-within-expressions feature.
Here is how the two together can be used to define a safe “maximum” macro that operates
on any arithmetic type and evaluates each of its arguments exactly once:
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
The reason for using names that start with underscores for the local variables is to avoid
conflicts with variable names that occur within the expressions that are substituted for a
and b. Eventually we hope to design a new form of declaration syntax that allows you to
declare variables whose scopes start only after their initializers; this will be a more reliable
way to prevent such conflicts.
Some more examples of the use of typeof:
• This declares y with the type of what x points to.
typeof (*x) y;
• This declares y as an array of such values.
typeof (*x) y[4];
• This declares y as an array of pointers to characters:
typeof (typeof (char *)[4]) y;
It is equivalent to the following traditional C declaration:
char *y[4];
To see the meaning of the declaration using typeof, and why it might be a useful way
to write, let’s rewrite it with these macros:
#define pointer(T) typeof(T *)
#define array(T, N) typeof(T [N])
Now the declaration can be rewritten this way:
array (pointer (char), 4) y;
Thus, array (pointer (char), 4) is the type of arrays of 4 pointers to char.
Compatibility Note: In addition to typeof, GCC 2 supported a more limited extension
which permitted one to write
typedef T = expr;
with the effect of declaring T to have the type of the expression expr. This extension does
not work with GCC 3 (versions between 3.0 and 3.2 will crash; 3.2.1 and later give an error).
Code which relies on it should be rewritten to use typeof:
typedef typeof(expr) T;
This will work with all versions of GCC.
这里大概叙述了typeof是一个什么东西,怎么用,实际上可以用简单的话来重述。如果你对sizeof很熟悉的话,那么大可进行类推,sizeof(exp.)返回的是exp.的数据类型大小,那么typeof(exp.)返回的就是exp.的数据类型。值得注意的是在上面的话里我们可以看到,如果编译选项中指定了使用标准C,那么gcc的扩展使用可能会受到影响,不过在gcc编译条件下使用__typeof__依然可以正常工作,这和使用asm是一样的。当然如果是在其他的编译器条件下,这样做也不行了,只能自定义一个macro去使用,也就是说跟gcc没啥关系了,你愿意把typeof咋实现都可以。
下面写一个小程序示例一下:)
#include
typedef struct
{
int x;
char y;
}astruct, * pastrcut;
int main()
{
int sizem, sizew;
int x = 3;
typeof(&x) m;
sizem = sizeof(m);
*m = 5;
typeof(((astruct *)5)->y) w;
sizew = sizeof(w);
w = ''a'';
return 1;
}
首先看main函数里的m变量,这个变量的类型就是typeof(&x), 由于x是int型的(这里与x是否被赋值一点关系都没有)所以&x应该是int *类型,那么typeof(&x)返回的类型就是int*,所以m自然也就是个int*类型的。之后我们看w变量,其类型是 typeof(((astruct *)0)->y), 其中astruct是一个被定义的结构类型,其中的y元素是char*类型,那么((astruct *)0)->y是啥意思呢?在这里0并不是真正的变量,可以把它理解为一个替代使用的符号当然这个符号最好是一个数,其意思更可以理解为一个被赋值了的变量,这个数可以不是0,3也可以8也可以,随便什么都可以。那么((astruct *)0)->y仅仅就是表示了y这个变量,所以typeof的结果就是y元素的类型,也就是char。
下面是gcc编译后使用insight调试的结果:
这里看到实际上对于编译后的程序,typeof完全是透明的,和直接使用变量类型定义变量没有什么太大区别。
下面是结果:
可以看到变量的工作是完全正常的。
在linux中这个东西很常用,这里举一个最常见的例子(linux2.6.23):
在list.h中有一个
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
这个宏是linux中使用的链表的一个方法。其中我们可以看到 container_of(ptr, type, member),这也是一个宏
在kernel.h中定义:
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );}
这里面就使用了我们上面示例中的一种typeof用法,其含义和上面所说的完全一样。
看过了typeof的用法和含义,我们不免想寻根溯源一下,那么就要看看gcc里面是怎么写的了。在gcc4.3的源码中(当然之前的gcc版本也都会有了:))在gcc目录下有c-parser.c文件,顾名思义,这个就是解析器了。那么我们看看里面到底对typeof写了什么。
在
static const struct resword reswords[]中列出了gcc的关键字,其中就有typeof
在
static void c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
bool scspec_ok, bool typespec_ok, bool start_attr_ok) 中对gcc的关键字进行了处理,
case RID_TYPEOF:
/* ??? The old parser rejected typeof after other type
specifiers, but is a syntax error the best way of
handling this? */
if (!typespec_ok || seen_type)
goto out;
attrs_ok = true;
seen_type = true;
t = c_parser_typeof_specifier (parser);
declspecs_add_type (specs, t);
break;
上面这一段就是对typeof的处理,RID_TYPEOF是在reswords[]中定义的typeof的ID,那么关键环节就在于
t = c_parser_typeof_specifier (parser);这一句上了。
/* Parse a typeof specifier (a GNU extension).
typeof-specifier:
typeof ( expression )
typeof ( type-name )
*/
static struct c_typespec
c_parser_typeof_specifier (c_parser *parser)
{
struct c_typespec ret;
ret.kind = ctsk_typeof;
ret.spec = error_mark_node;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF));
c_parser_consume_token (parser);
skip_evaluation++;
in_typeof++;
if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
{
skip_evaluation--;
in_typeof--;
return ret;
}
if (c_parser_next_token_starts_typename (parser))
{
struct c_type_name *type = c_parser_type_name (parser);
skip_evaluation--;
in_typeof--;
if (type != NULL)
{
ret.spec = groktypename (type);
pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
}
}
else
{
bool was_vm;
struct c_expr expr = c_parser_expression (parser);
skip_evaluation--;
in_typeof--;
if (TREE_CODE (expr.value) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
> error ("% applied to a bit-field");
ret.spec = TREE_TYPE (expr.value);
was_vm = variably_modified_type_p (ret.spec, NULL_TREE);
/* This should be returned with the type so that when the type
is evaluated, this can be evaluated. For now, we avoid
evaluation when the context might. */
if (!skip_evaluation && was_vm)
{
tree e = expr.value;
/* If the expression is not of a type to which we cannot assign a line
number, wrap the thing in a no-op NOP_EXPR. */
if (DECL_P (e) || CONSTANT_CLASS_P (e))
e = build1 (NOP_EXPR, void_type_node, e);
if (EXPR_P (e))
SET_EXPR_LOCATION (e, input_location);
add_stmt (e);
}
pop_maybe_used (was_vm);
}
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
return ret;
}
这些就是这个函数的处理过程。虽然内容看起来有些复杂,但是仔细观察就会看到其中有一句很值得我们注意:
ret.spec = TREE_TYPE (expr.value); 这是一句对返回值的赋值语句,也就是说,这句话的结果就是返回值的内容的一部分,expr是前面c_parser_expression (parser);的处理结果,得出的是表达式的内容,TREE_TYPE是tree.h中的一个宏:
/* In all nodes that are expressions, this is the data type of the expression.
In POINTER_TYPE nodes, this is the type that the pointer points to.
In ARRAY_TYPE nodes, this is the type of the elements.
In VECTOR_TYPE nodes, this is the type of the elements. */
#define TREE_TYPE(NODE) ((NODE)->common.type)
可以看出这个宏的结果大概就是一个type了,那么ret的结构里一共就有两个内容,一个是spec一个就是kind,kind在前面已经赋值过了,
/* A typeof specifier. */
ctsk_typeof
这个东西是C-tree.h中enum c_typespec_kind的里的一项,因此,ret的关键内容就是spec,由此我们就大概了解到,解析typeof的函数的返回结果就是一个type,这样我们也就理解了typeof的真正含义了。
好了,上面就是一些对于typeof的回顾,本人水平有限,谬误之处还请大家指正,谢谢!