C语言的符号表和类型系统2

本文探讨了编译原理中的符号表与类型系统构建方法,详细解析了C语言中的枚举类型、结构体变量等复杂类型如何在符号表中记录,并介绍了类型系统链表的设计与实现。

阅读博客的朋友可以到我的网易云课堂中,通过视频的方式查看代码的调试和执行过程:

http://study.163.com/course/courseMain.htm?courseId=1002830012

这一节,我们继续就上一节讨论的内容,继续就符号表和类型系统的构建进行深入的探讨。

基于上一节的基础,我们看看,编译器如何为一个变量构建它在符号表中的记录和类型系统。假设我们的C语言代码中有如下的变量定义:

enum rabbits {
FLOSPY, MOPSEY, PETER
};

上面的枚举类型,会被C编译器转换成如下形式的C代码:

const int FLOSPY = 0;
const int MOPSEY = 0;
const int PETER = 0;

编译器在解析 “const int FLOSPY = 0” 这条语句后,会在符号表中产生以下数据结构:

这里写图片描述

由于变量FLOSPY 没有*, 没有[]之类的类型声明,因此它的类型系统只需要说明符,不需要修饰符,因此它的类型列表中就是由一个specifier.它的数据类型是INT, 由于被初始化成一个常量整形,所以constantValue设置为0.大家注意,从Symbol对象引出两个箭头,两个箭头都指向specifier对象。之所以需要两个箭头是因为,类型系统本质上就是一个链表,链表链接的是两种对象,一种是declarator, 一种是specifier, 我们在实现代码的时候,需要把declarator放在链表的前面,specifier放在链表的最末尾。因此从symbol发出的两个箭头,一个指向队列的开头,这样从这个箭头起始就可以逐个访问链表的每个对象,由于declarator放在链表的前面,这样从这个箭头开始,就可以访问一系列的declarator, 但是如果我们想要访问链表最末尾的specifier对象,那就得遍历整个链表,这样效率就太慢了,于是,从symbol对象引出第二个箭头直接指向链表的末尾,也就是specifier对象,这样想要直接访问类型系统链表的specifier,直接从第二个指针读取就可以了,不需要遍历整个链表,例如下面这个例子:

long int (*Frollo)[10];

编译器会给上面的变量声明建立如下的符号表记录和类型队列:

这里写图片描述

大家看到,类型系统队列有三个元素,前两个是declarator, 最后一个是specifier, 从symbol引出的两个箭头,一个指向链表的开始,第二个直接指向链表的末尾,也就是specifier.

这样,我们就需要在代码中,设计一个链表来讲declarator 和 specifier连接起来,这个链表的代码如下:


public class TypeLink {
    boolean  isDeclarator = true; //true 那么该object 的对象是declarator, false那么object指向的就是specifier
    boolean  isTypeDef = false; //true,那么当前变量的类型是由typedef 定义的

    Object   typeObject;

    TypeLink  next = null;

    public TypeLink(boolean isDeclarator, boolean typeDef, Object typeObj) {
        this.isDeclarator = isDeclarator;
        this.isTypeDef = typeDef;
        this.typeObject = typeObj;
    }

    public Object getTypeObject() {
        return typeObject;
    }

    public TypeLink toNext() {
        return next;
    }
}

这样我们在Symbol类中,要添加两个成员变量:

public class Symbol {
    String  name;
    String  rname;

    int       level;  //变量的层次
    boolean   implicit;  //是否是匿名变量
    boolean   duplicate;   //是否是同名变量

    Symbol    args;   //如果该符号对应的是函数名,那么args指向函数的输入参数符号列表

    Symbol    next;  //指向下一个同层次的变量符号

    TypeLink  typeLinkBegin;
    TypeLink  typeLinkEnd;
}
Struct 类型变量的类型系统

在Specifier 类中,最后一个成员变量StructDefine,我还没有解释,这个类型是专门用于处理Struct类型声明的。由于一个结构体里面包含了多种变量声明,所以结构体变量的存在使得类型系统复杂了很多,我们先看看结构体变量类型的代码:

/*
 * struct argotiers {
 *     int  (*Clopin)();
 *     double  Mathias[5];
 *     struct  argotiers*  Guillaume;
 *     struct  pstruct {int a;} Pierre; 
 * }
 */
public class StructDefine {
    private String tag; //结构体的名称,例如上面的例子中,对应该变量的值为 "argotiers"
    private int  level; //结构体的间套层次
    private Symbol fields; //对应结构体里的各个变量类型

    public StructDefine(String tag, int level, Symbol fields) {
        this.tag = tag;
        this.level = level;
        this.fields = fields;
    }

    public String getTag() {
        return tag;
    }

    public int getLevel() {
        return level;
    }

    public Symbol getFields() {
        return fields;
    }
}

我们以一个具体的结构体声明例子为例,看看它对应的符号表和类型系统是怎样的:

struct  argotiers {
    int  (*Clopin)();
    double   Mathias[5];
    struct   argotiers *Guillaume;
    struct   pstruct {int a;} Pierre;
} gipsy;

它的类型系统如下:
这里写图片描述

这个系统看起来似乎很复杂,但实际上它是由若干个简单的类型系统结合而成的,搞清楚前面我们描述的类型系统队列,对这个图的理解应该不难,这个图其实也表明,任何复杂的的系统,都是由多个简单的单元相互勾连交叉所形成的。

这两节的代码主要用于解释概念,在实际开发时,我们会根据需要,对当前代码做相应修改,下一节,我们基于前面的变量声明解析过程,看看语句:
long int *x, y;

所声明的两个变量,他们的类型系统和符号表是如何建立的。

<think>嗯,用户想了解C语言符号表的相关信息。之前系统已经给出了清晰的引用资料,需要整合这些内容给出专业且易懂的解释。 用户可能是编译器相关的学习者或开发者,提问很专业但缺乏具体方向。从引用[1][3]看,需要重点解释符号表如何存储C语言特有的复杂类型信息,比如结构体内部的函数指针多维数组。特别要注意引用[2]提到的符号表与语法分析的区别——用户可能混淆了这两个阶段。 考虑设计四个核心部分的讲解:基本信息、存储内容、类型系统关联、结构设计。开头先用一句话定义符号表是"编译器存储标识符属性的数据库",这样最直观。在类型系统部分要强调C语言的独特性,比如struct argotiers那个例子很典型,可以拆解它的三个成员来说明符号表如何递归存储嵌套类型。 技术细节上需要解释修饰符(declarator)的解析过程。引用[1]提到的"说明符+修饰符"模式是关键,比如int (*a)[10]就要拆解为基本类型int两层修饰符(指针+数组)。可以画个示意图说明符号表条目如何像洋葱一样层层包裹这些修饰符。 最后考虑补充两个实际价值点:为什么Java的符号表设计更简单(C缺少垃圾回收等机制),以及调试器中符号表的作用。这样能帮助用户理解抽象知识的应用场景。 准备用加粗标题分段呈现,把struct argotiers的案例单列强调。避免直接贴代码但可以描述数据结构字段,结尾的关联问题要引导用户关注实现难点而非表面功能。 </think>以下是对C语言符号表的详细介绍,结合引用内容的核心要点: --- ### **一、符号表的基本作用** 符号表编译器在**语义分析阶段**的核心数据结构,用于存储程序中所有**标识符的属性信息**,包括: 1. **变量名**、函数名、结构体/联合体标签 2. **类型信息**(基本类型、指针、数组、结构体等) 3. **存储类别**(`extern`、`static`、`auto`) 4. **作用域与生命周期** 5. **内存地址**(编译期或链接期确定) > 引用[2]明确其核心任务:**记录所有声明(变量/函数),并在语义层面检查合法性**。例如 `int 1;` 在语法分析阶段可通过(因符合 `int [token]` 规则),但语义分析会因 `1` 非合法标识符而报错[^2]。 --- ### **二、符号表存储的关键内容** | **条目类型** | **存储信息示例** | **说明** | |--------------|--------------------------------------|-----------------------------------| | 变量 | `int x;` → 类型`int`, 地址, 作用域 | 支持基本类型复杂类型 | | 函数 | `void f(int);` → 返回值类型、参数列表 | 包含参数数量类型检查 | | 结构体 | `struct S {int a;};` → 成员列表 | 递归存储嵌套结构(如成员的类型) | | 类型别名 | `typedef int MyInt;` → 等价类型映射 | 建立自定义类型到基础类型的关联 | > 引用[3]的案例 `struct argotiers` 展示了符号表如何递归存储**嵌套类型**(如函数指针 `int (*Clopin)();` 多维数组 `double Mathias[5];`)[^3]。 --- ### **三、类型系统的关键设计** C语言的类型声明由两部分构成(引用[1]): 1. **说明符(Specifier)** - 基础类型:`int`, `long`, `char` - 存储类别:`extern`, `static` - 复合类型:`struct/union/enum` 标签 2. **修饰符(Declarator)** - 组合操作符:`*`(指针), `[]`(数组), `()`(函数) - 变量名:如 `*a`, `b[10]` **符号表需动态组合二者**。例如: - `int (*p)[3];` → 说明符 `int` + 修饰符 `(*p)[3]` - 符号表需解析出:**指向3个int数组的指针** --- ### **四、符号表的数据结构设计** 由于C语言的**类型组合复杂性**(如多级指针、嵌套结构体),符号表需采用灵活设计: 1. **树形结构** - 根节点:标识符名称 - 子节点:类型属性(指针层级、数组维度、结构体成员链表) 2. **类型图(Type DAG)** - 避免重复存储相同类型(如多个 `int*` 指向同一类型节点) 3. **作用域栈** - 实现块作用域:进入作用域压栈,退出时弹出局部符号 > 引用[4]强调:符号表实现**高度依赖编译器设计目标**,可扩展为支持多语言[^4]。 --- ### **五、符号表的实际应用场景** 1. **语义检查** - 检查类型兼容性(如赋值 `int*` 到 `char*` 是否合法) 2. **中间代码生成** - 为变量分配临时地址或寄存器 3. **调试信息** - 生成调试符号(如GDB使用的DWARF格式) 4. **链接器符号解析** - 解决跨文件的 `extern` 引用 --- ### 典型符号表条目示例(以结构体为例) ```c struct argotiers { int (*Clopin)(); // 成员1:函数指针 double Mathias[5]; // 成员2:double数组 struct argotiers *Guillaume; // 成员3:指向自身的指针 } gipsy; // 全局变量 ``` 符号表需递归存储: 1. `gipsy` 的类型为 `struct argotiers` 2. 成员 `Clopin` 的类型为 **函数指针** → `int (*)()` 3. 成员 `Guillaume` 的类型为 **递归指针** → `struct argotiers*` ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值