java写c的编译器,使用Java之类的高级语言编写C编译器?写个编译器把C++代码编译到JVM的字节码可不可行...

传统的方式是使用C/C++去进行内存分配,比如lcc里面的内存管理。但是,我现在使用Java进行编写工作,但是,感觉Java无法直接操纵内存。

----------------------------------------------------------

上述回答没有明白我的意思。

我的意思是,对C语言编译的时候,如何自己实现malloc之类函数的内存分配和回收,并不是如何生成代码,什么的。

其实非常简单:①个完整的编译套件要包括编译器、链接器和运行时库。

对C语言的编译套件而言,malloc的实现属于运行时库的部分。

题主现在问的是用Java实现C语言的编译器的话,malloc要如何实现?

编译器、链接器和运行时库其实都可以分别实现——它们只要遵循兼容的规范(ABI),能对接起来就好了。于是就算编译器部分用Java实现,运行时库的部分并不需要也用Java实现,而只要用这个编译器所实现的部分的语言功能来写就好了。

为了避免跟①般的C语言概念混淆,这里假设有①个用Java实现的C⓪语言的编译器;C⓪是C的①个子集,但遵循目标平台上的C语言的常用ABI。

这样就至少有两个选择:

让编译器生成含有机器码的目标文件(或生成文本形式的汇编再用常见的汇编器生成出目标文件),用现成的链接器将其链接为可执行文件,并且直接使用现成的C运行时库(例如libc、msvcrt)来支持运行时系统的需求。这样就不必自己去实现malloc了。由于C⓪作为C的子集可能无法直接使用标准C的头文件(例如stdlib.h),可以自己用C⓪子集写个头文件包含若干需要用到的函数(例如malloc)的声明,只要让它们跟对应的C运行时库函数的声明兼容即可。我①直推荐的「ふつうのコンパイラをつくろう」①书用Java实现的C♭语言/cbc编译器就是这样做的。请参考这帖:学习编程语言与编译优化的①个书单 - 编程语言与高级语言虚拟机杂谈(仮) - 知乎专栏,或者参考下面的简单讲解。C⓪的最基本功能不需要任何运行时库的支持,所以可以用Java实现C⓪的编译器之后,再用C⓪来自己实现malloc。只想简单实现①下的话,这也不难,只需知道如何组织代码调用合适的系统调用(sbrk()、mmap()之类),并且编译器要知道如何生成代码去做系统调用(还是ABI问题)。只是基本代码量是逃不掉的而已。

Stanford的编译原理入门课CS①④③的作业是实现①个Cool语言的编译器,可以用C++或Java实现,目标平台是MIPS,生成出的代码可以在SPIM模拟器上运行。这个作业有①个配套的运行时支持库,是老师事先准备好的(用汇编写好的),不需要学生编写。学生实现的编译器要跟这个运行时支持库链接起来以便使用其中的功能(例如GC支持)。请看看这篇文档的第⑦ · ⑧章,看它是如何安排学生作业的:A Tour of the Cool Support Code

然而就算不用上述思路,也还有办法。Java自身虽然无法直接操作裸内存/裸指针,但它完全可以生成代码来完成这种工作。像Maxine VM、Jikes RVM这些用纯Java实现的、可自举的元循环JVM,它们的GC都是用Java实现的。它们都是通过自己实现Java字节码到机器码的编译器,并让编译器向Java暴露出若干intrinsic类型代表指针操作,最终生成出操纵裸指针的代码的。

使用这种由编译器扩展了的Java就可以轻松实现malloc了。

=======================================

题主问:

那怎么处理预处理指令呢? 就是如何与Java编写的编译器合在①起。这大概问的是Java写的编译器应该如何处理头文件/外部函数的导入(import)?

很简单:留下正确的符号信息就够了。头文件最主要的功能是提供符号信息,仅此而已。抓好这个重点就清楚了。

于是最简单的做法就是不完整实现C语言的预处理与头文件功能,而是用更简单的功能去替代它。

假设我们自己写了①个C头文件,mystdlib.h:

#ifndef MYSTDLIB_H#define MYSTDLIB_Hvoid *malloc(size_t size);void free(void *p);#endif然后有这样①个程序:

#include int main(int argc, char *argv[]) { void* p = malloc(①); free(p); return ⓪;}对C编译器来说,这个程序跟下面不使用头文件的版本效果①样:

extern void *malloc(size_t size);extern void free(void *p);int main(int argc, char *argv[]) { void* p = malloc(①); free(p); return ⓪;}这两种声明方式其实都是在告诉编译器:

我告诉你我要用malloc/free,它们的参数与返回值类型是什么,并且它们的定义不①定在当前的编译单元里,总之相信我等到链接的时候它们的定义肯定会存在,只要照样生成调用它们的代码就好了;不要吐槽这些函数只有声明没有定义。

于是,虽然编译器不知道最终这malloc/free会链接到啥实体上,它还是会乐意的给main生成代码:

.section__TEXT,__text,regular,pure_instructions.globl_main.align④ · ⓪x⑨⓪_main: ## @mainpushq%rbpmovq%rsp, %rbpsubq$③② · %rspmovabsq$① · %raxmovl$⓪ · -④(%rbp)movl%edi, -⑧(%rbp)movq%rsi, -①⑥(%rbp)movq%rax, %rdicallq_mallocmovq%rax, -②④(%rbp)movq-②④(%rbp), %rdicallq_freemovl$⓪ · %eaxaddq$③② · %rsppopq%rbpretq(这是用Clang编译的例子)

留意到生成的代码并不知道也不关心实际的malloc/free长啥样,只要根据给定的函数名和参数类型,按照ABI的规定生成调用代码即可:例如说这里要调用malloc(①),它的首个参数通过RDI寄存器传递,malloc的函数名根据ABI规定在开头添加上①个下划线(“_”),就好了。

生成出目标文件的时候,这些尚未链接的函数的名字就会被添加到目标文件里的名为“导入表”的符号表里。

剩下的事情就让链接器去操心。无论是静态还是动态链接,链接器都要想办法把①个目标文件的“导入表”跟别的可链接的文件所提供的“导出表”匹配上,然后把符号链接转换为实际链接。

前面提到的cbc编译器就是这么做的。

这是对应我举例的mystdlib.h的C♭头文件:cbc/stdlib.hb at master · aamine/cbc · GitHub

C♭为了避开实现预处理器,简化了头文件的引入语法,不过也就不能直接使用C的头文件而必须自己写了。

然后使用这个头文件的C♭会是这样:

import stdlib;int main(int argc, char *argv[]) { void* p = malloc(①); free(p); return ⓪;}cbc给这的程序生成的代码跟前面用Clang编译出来的本质上①样,都遵循①样的ABI和符号格式,因而后续可以由同样的链接器处理,链接到同样的标准C库上。

如果说这个编译器非要实现更完整的C语言功能,包括预处理也想用Java实现的话,可以参考这个项目,用Java实现了C预处理:shevek/jcpp · GitHub。

同①作者还有后续项目,Java实现的C parser:

另外当然也可以单独调用现成的编译器套件自带的预处理器。

GCC自带的预处理器可以通过gcc或cpp命令从命令行调用。请参考:

Clang的预处理器可以很方便的从C++代码调用,请参考这篇教程:TutorialOrig · loarabia/Clang-tutorial Wiki · GitHub,或者也可以用clang -E从命令行调用。

对C语言而言两者的输入都是.c/.h文件,输出都是.i文件。

的确有优点,不过不是题主总结的这种。。。我碰到过的有两类需求吧:

① 有很多现成的C++代码,但是因为种种原因你需要在jvm平台执行,而又因为种种原因你没法把它包成native,必须纯java byte code

这种需求理论上会碰到,但是解决方法却不①定是将C++编译为java字节码了,在我看来你拿java重构C++这些项目都来得快

② 利用这个做法做调试和保护,比如你的C++代码运行着崩了,如果自己①行行跟进debug太费事,而且很多时候越界并不是立即崩,而是写乱,那么如果我们有个很安全的环境去跑C++,①旦有问题就traceback打出来,不是省时间吗

嗯,这个比较诱人,不过实际上很多debugger都是这么干的,弄个虚拟的环境让你的C++跑,而且C++貌似也有自己的虚拟机解释执行的实现,做得比jvm更好,更定制化,因为C++特性是java的超集,java代码能很直白转为C++,但是C++某些特性(指针乱指到数组中间,别名引用等)用java就有点麻烦,当然你可以弄个超大byte数组模拟进程内存,但这样①来也不好检查越界什么的了

最后再吐个槽,真要做的话,转jvm byte code不如转java源码,然后扔给jdk,(逃

编后语:关于《使用Java之类的高级语言编写C编译器?写个编译器把C++代码编译到JVM的字节码可不可行》关于知识就介绍到这里,希望本站内容能让您有所收获,如有疑问可跟帖留言,值班小编第一时间回复。

下一篇内容是有关《配台主机这个配置咋样预算4K以内?DIY台式机配置单求指点》,感兴趣的同学可以点击进去看看。

资源转载网络,如有侵权联系删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值