#头条创作挑战赛#

scf,是我用C语言写的一个编译器框架。

它可以把类似C语言的简单代码编译成可执行文件,暂时只支持x64的CPU和Linux系统。

语法的设计基本上参照了C语言,有一点点的改动:

1,取消了typedef 这类因为C语言的历史遗留而导致的关键字。

2,结构体类型在定义变量时不需要带struct关键字。

C语言如果没有提前用typedef 声明结构体类型的话,在定义变量时必须带struct,例如:

struct A* p;

不能直接用A* p; 除非前面用typedef struct {} A; 声明过A。

这个问题也是C语言的历史遗留问题。

实际上在语法分析时直接忽略掉声明语句中的struct并不难,C++就是这样的,但C语言或许是为了版本兼容还是添加了typedef关键字。

我在scf里把它去掉了,只在定义结构体时才需要struct,在定义变量或指针时不需要struct。

3,对结构体的所有操作c++结构体初始化,在实现上都通过指针。

结构体,实际上是个成员变量的字节数不固定的“数组”。

struct A {

double d;

int i;

char c;

};

int a[3];

结构体A和数组a都有3个成员,区别只是结构体的成员的字节数不一样,而数组的所有成员的字节数一样。

访问结构体的元素,实际上跟访问数组的元素差不多,都是首地址+偏移量。

只是数组的偏移量,是单个成员的字节数*成员的索引号。

结构体的偏移量,是当前成员前面的其他成员加起来的字节数。

如果结构体当函数实参的话,传指针就行,额外把结构体复制一份没意义。

传参、取结构体的成员等场景,scf全使用的指针,不支持复制结构体。

所以,结构体的运算符一律使用的->,没有使用点号.

即使这样:

struct A { int x, y; };

A a;

也是a->x = 1;

而不是C语言的a.x = 1;

我在scf框架的底层没有区分这两种运算,不管是结构体取成员、还是结构体的指针取成员,都一律按结构体的指针取成员。

实际上就算是a.x = 1,到了生成机器指令的时候:也是先加载a的地址,然后根据x的偏移量赋值为1,实际还是(&a)->x = 1.

C语言对->和.做区分,我觉得还是为了后端考虑:

使用p->x = 1是指针,生成机器指令直接计算p与x的偏移量就行;

使用a.x = 1是结构体,生成机器指令时要先加载a的地址,然后再计算&a与x的偏移量;

不同的运算符,在这里是很强的提示信息,后端代码要好写一些。

如果使用同样的运算符->,编译器就要记录p和a的变量类型(p是指针,a是结构体),并做区分处理。

但是,编译器本来就要记录变量类型的(这个区分并不难),所以我就把点号运算去掉了[呲牙]

从少打字的角度考虑,->和.里选点号更好。

但是C程序员都打惯了->了,所以我还是选了->。

4,结构体的赋值运算,

结构体的赋值,和结构体指针的赋值解引用,我在设计语法时纠结了很久!

例如:

A* p;

A* q;

A a;

A b;

p和q是结构体的指针,a和b是结构体。

如果a和b之间赋值的话c++结构体初始化,可以这么写a = b;

如果p和q之间赋值的话,就存在2个语义:

1)指针赋值,

p = q;

不涉及到结构体成员的拷贝,只是让8个字节的指针变量p = q就行。

2)指针的赋值解引用,实际是结构体赋值。

例如,*p = *q;

它的抽象语法树如下:

c s结构与b s结构 松鼠科学会_c++结构体初始化_c二维数组初始化为0

*p = *q的抽象语法树

要想处理这种情况,不得不去查看=两边的类型。

两个星号*的运算结果都是结构体,而不是结构体的指针。

但是,这种情况没法直接处理等号=的两个子节点,而必须接着往下找,才能找到真正要处理的变量p和q。

c++结构体初始化_c s结构与b s结构 松鼠科学会_c二维数组初始化为0

a=b和p=q的抽象语法树

并且,a = b, p = q, *p = *q,这三个的抽象语法树非常的接近,在处理时还得判断等号的两边到底是结构体、结构体指针、还是星号运算符。

我一开始并不想做这么复杂的判断,纠结了好久也没想到捷径,最后还是把语法设计成这样了。

另外,赋值运算符还可能被重载了,也需要在这里考虑。

所以,赋值运算符=的语义分析也写得比较复杂。

scf编译器只有1种情况直接使用结构体,而不是使用它的指针,就是对结构体的赋值:

struct A { int x, y; };

A a, b;

a = b;

A c = {1, 2};

毕竟结构体类型的变量占的内存,跟结构体的指针占的内存字节数不一样。

其他情况都是使用结构体的指针,例如:传参,访问成员变量,指针赋值,etc.

5,结构体的声明和定义,都必须在全局作用域。

不支持在结构体定义里再嵌套成员结构体的定义。

但是,支持在结构体定义里嵌套结构体类型的成员变量:这个不得不支持,否则数据结构就没法写了[捂脸]

如下这样的代码是不支持的:

struct A{

struct B {} b;

struct C {};

C c;

};

虽然C语言支持,但我觉得没必要搞这么复杂:

1)把所有结构体的定义都放在全局作用域,反而更简单。

2)把结构体类型的定义和结构体变量的声明分开,反而更简单。

所以,我设计的语法是如下这样的:

struct B {};

struct C {};

struct A {

B b;

C c;

};

6,运算符和构造函数的重载,

为了让数学公式写起来方便,我加了运算符和构造函数的重载。

但是,没有支持虚函数、继承、封装,这3个OOP机制。

C++的祖传代码,都是因为虚函数、继承、封装[捂脸]所以我把这3样全去掉了。

我觉得,保留运算符和构造函数的重载就可以了。

至于OOP机制,还是用结构体+函数指针吧[呲牙]

struct A;

struct Aops {

int open(A** pp);

void close(A* p);

};

struct A {

Aops* ops;

void* priv;

};

我觉得,C风格的结构体+函数指针的OOP就很好。

尽量把A->ops的初始化放在open()函数里,不要半截里再改变。

半截里改变函数指针,是很让人烦的。

7,去掉了头文件,也没支持宏,

如果一个文件里写得只是函数声明和类型定义,那么它就是头文件。

没有特意用.c和.h区分。

任何2个文件之间都可以包含,只要不构成递归包含!

所以,只保留了include用来包含文件,而且把它作为了关键字。

C语言的花样宏定义,我都把它去掉了。

8,struct和class这两个关键字,是完全没有差别的。

C++里的struct和class也只是封装权限的差别。

我既然把封装去掉了,这两个就没有差别了。

9,自动内存管理,

scf有两种内存管理模式:自动管理,手动管理。

如果是使用create关键字创建的(结构体)类对象,那么scf会自动管理内存:

在变量离开作用域时,会调用程序员写的__release()函数释放内存。

scf的类的构造函数和析构函数:

int __init(A* this, …); // 构造函数

void __release(A* this); // 析构函数

一个叫__init(),一个叫__release()。

为了不使用异常,构造函数有个int返回值,可以返回错误码。

析构函数就只能是void了,毕竟就算有错误码,程序也没法收到。

如果是malloc / calloc 创建的,那就手动free()吧。

如果不使用create关键字创建结构体(类)对象,那么就可以纯手动管理内存:不会触发自动内存管理。

10,协程,

我倒是写完了协程的模块代码,但一直没整合到scf里。

一旦使用了协程,程序员就必须在main()函数里主动运行协程的主线程函数。

总的来说,协程更像个编程框架,而不是编程语言。

int main()

{

co f0();

co f1();

co_run();

return 0;

}

这种代码并不会马上运行,而是在最后执行co_run()时才会运行。

co f0() 和 co f1() 只是给框架添加协程对象的结构体,相当于给epoll框架添加事件,最后还是要在末尾运行epoll_wait()时才会真正启动事件的处理。

在底层的语言里,协程都是作为框架的,而不是语言本身的机制。

11,最后给个main()函数,

c s结构与b s结构 松鼠科学会_c二维数组初始化为0_c++结构体初始化

main()函数

输入输出还是使用的C标准库:

不使用不行,否则就得自己去通过write()系统调用去打印hello world了。

1)scf的代码在gitee上,见我的置顶微头条,

2)拉下来之后,在parse目录下直接make,就可以获得编译器的可执行文件scf,它也在parse目录。

3)用命令./scf hello.c编译上图的代码文件,默认会生成2个文件:

1.elf,是连接前的.o文件,

1.out,是连接后的可执行文件,

用chmod +x ./1.out 给它加上运行权限,

然后运行./1.out 就可以打印出hello world了。

PS:大家如果发现BUG了记得给我说一下,我去修改scf的代码!

连接后的程序头和动态库信息

main函数的汇编指令和运行结果

连接进来的C动态库函数

限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: muyang-0410