typedef int a;        //a是TypedefNames,作用域为all the scope
//overload:表示在嵌套作用域中,类型定义名是否作为变量,而不是类型名
int f(char a)    //由于之前a已经是typedef了,所以这里标记为overload, 
//那么在函数f的作用域中只能作为变量,而不是类型名,那么下面用a作为类型来定义变量都是语法错误
{
    
//语法分析时typedef是声明,a已经被声明为char了,所以这里报告a重声明了
    
//typedef char a;        
    printf("%c", a);
    
return 0;
}

int main()
{
    typedef 
char* a;    //在mian的作用域里面,a就是char *类型了
    a x = (void*)0;
    printf(
"Hello, World %c\n");
    
return 0;
}

引用ucc的文档:
在大多数情况下,C语言是一个LL(0)文法,解析器可以根据当前的记号而决定与什么产生式和非终结符匹配。但是由于C语言引入了类型定义而破坏了这个特性。比如说在复合语句中遇到一个标识符,如果是类型定义名,则应该为声明,否则为语句。为了简洁性,ucc将语法分析和语义检查分成两个独立的阶段,但是类型定义又要求在语法分析中做一定的语义检查,为此,ucc在语法分析中对于类型定义做最小的检查。对于语法分析器来说,只要知道一个标识符是类型定义名就足够了,而不需要知道具体的类型。
语法分析中用以进行语义检查的数据结构和算法如下:
typedef struct tdname
{
    char *id;
    int level;
    int overload;
} *TDName;
tdname表示一个类型定义名。
 id: 名字
 level: 类型定义名被定义点的嵌套层次,文件作用域的嵌套层次为0,复合语句的起始处嵌套层次加1,复合语句的结束处嵌套层次减1。有可能在多个作用域中定义同一个类型定义名,level表示这些作用域中嵌套层次最小的。比如说对于如下代码片段:
typedef int a;
int f(void)
{
     typedef int a;
}
其中level值应为0。这样在其它函数中a的定义也是可见的。
overload:表示在嵌套作用域中,类型定义名是否作为变量,而不是类型名。比如如下代码片段:
typedef int a;
int f(int a)
{
}
在f的函数定义中,a是参数,而不是类型定义名。
ucc使用两个向量:TypedefNames记录了所有定义的类型名。OverloadNames记录当前作用域中被重载了的类型名。
对于每一个声明,调用CheckTypedefName函数,如果该声明属于类型定义,则查看相应的类型名是否存在,如果不存在,则加入新的类型定义,如果存在,修改level为最小的嵌套层次。如果该声明不属于类型定义并且该声明所定义的变量已定义为外部作用域的类型名,则标记该类型名被重载,并且将其加入OverloadNames中。
每当一个复合语句结束时,重置OverloadNames中的所有类型名的重载状态。