又见typeof

来源:互联网 发布:大智慧软件官方网站 编辑:程序博客网 时间:2024/05/29 17:50

    最近偶然又看到了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 <stdio.h>

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 ("%<typeof%> 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的回顾,本人水平有限,谬误之处还请大家指正,谢谢!