如果一个struct tag和一个typedef定义的类型名相同,会怎么样?不会有编译错误,但是理解起来,尤其是不熟悉的话,还是有一点困难。看下面的例子。
#include
#include
#include "assert.h"
#include "mem.h"
//#include "list.h"
#ifndef LIST_INCLUDED
#define LIST_INCLUDED
#define T List_T
typedef struct T *T;
/* C语言中,所有的结构体、联合体、枚举类型的名字共享一个名字空间;所有变量名、函数名和类型名(指typedef定义的类型名)共享另外一个名字空间。所以
typedef struct List_T *List_T;
这样的typedef是合法的,不会有名字冲突。但是这样定义仍然会带来一定的理解上的困难。下面结合具体实现详细说明。注意#define定义的宏在进一步编译之前做字符串替换。
*/
struct T {
T rest;
/* 注意在上面的成员定义里T被替换为List_T,C语言中不能将结构体的名字作为类型名直接使用,因此此处只能把List_T解释成为typedef定义的类型名,rest的类型是struct List_T *。
*/
void *first;
};
extern T List_append (T list, T tail);
extern T List_copy (T list);
extern T List_list (void *x, ...);
/* irrelevent code lines omitted */
#undef T
#endif
#define T List_T
T List_push(T list, void *x) {
T p;
NEW(p);
p->first = x;
p->rest = list;
return p;
}
T List_list(void *x, ...) {
va_list ap;
T list, *p = &list;
/* 在上面的定义里,p的类型是T *。T在宏替换过程中被替换为List_T。注意这里的List_T不是结构体名字用作类型名(这在C语言中是不允许的),那么List_T必须被解释为typedef定义的类型名。p的类型是List_T *,即struct List_T **。
*/
va_start(ap, x);
for ( ; x; x = va_arg(ap, void *)) {
NEW(*p);
/* NEW这个宏是在mem.h中定义的:
#define NEW(q) ((q) = ALLOC((long)sizeof *(q)))
为了方便说明,宏ALLOC的具体实现不再详细解释,可以认为ALLOC基本上和malloc的作用是一样的。
结合上述对变量p的类型分析可知,NEW(*p)中,宏参数q的类型和*p的类型是一样的,即struct List_T *。所以宏定义中sizeof *(q)取得的size值,是对q的类型再次解引用之后的struct List_T类型的size。
上述的宏展开后的结果是:
((*p) = ALLOC((long)sizeof *(*p)));
这个宏所做的工作就是分配一个新的struct List_T类型的结构体,并且让*p指向它。
*/
(*p)->first = x;
p = &(*p)->rest;
}
*p = NULL;
va_end(ap);
return list;
}
阅读(1284) | 评论(0) | 转发(0) |