有关详细说明和代码调试演示步骤,请参见视频
用java开发c语言编译器
C语言有非常基础、极其重要的函数。 是压缩的。 此函数的作用是计算变量占用的内存大小。 如果知道对应变量的大小,就可以动态分配对应大小的内存。
如果sizeof计算的对象是int、char等简单的基本类型变量,则可以很容易地计算他们的大小。 因为假设当前代码在32位计算机上运行,所以如果传递给sizeof的变量是int,如果是指针类型,则该变量的内存大小为4字节,如果变量类型为short,则该内存大小为2字节
因此,在实现sizeof时,通过从传入的变量名中找出与其对应的Symbol对象,从Symbol对象中找出其数据类型,就可以从上述分类中得知该对象的内存大小。
问题是,传递sizeof可能符合数据类型。 这意味着传递的变量可以是结构。 例如,以下结构:
struct TAG {
int p;
char c;
int arr[3];
(;
因为结构体的内存大小实际上是构成该结构体的内部成员的大小之和,所以为了计算结构体TAG的内存大小,需要计算并合计p、c、arr[3]个成员的大小。
更难的是结构间覆盖,即一个结构中包含结构的成员。 这样,sizeof必须是分层递归方法才能正确实现。
我们使用深度优先搜索实现sizeof。 深度优先搜索主要用于图的扫描,这里尝试将该算法平移到sizeof的计算中。 深度优先搜索的基本原理如下,假设有向图:
遍历上面的有向图表时,如果被访问的节点包含子节点,则首先访问其子节点,然后访问该节点或该节点的同级。 在上面有向图中,深度优先遍历的顺序是1-2-3-4-3-2-5-6-5-1
同样,在实现sizeof函数时,如果传入的变量是结构体,首先用sizeof计算结构体各成员的大小,然后合计各成员的大小,得到当前结构体的大小。
通过此sizeof函数实现的基本逻辑如下。
1、传递的变量为基本数据类型时,直接返回该变量的字节大小。
2 )如果传递的变量是结构体,则再次调用sizeof计算各成员的大小,合计得到当前结构体的大小。
3、当前变量为数组型时,将前2步计算的大小乘以数组的元素个数,即可得到当前变量的大小。
由于sizeof是库函数,因此其实现为ClibCall.java,实现代码如下:
公共类clib call {
私有设置助手;
私有clib call (
apiSet=new HashSet (;
apiset.add(printf );
apiset.add(malloc );
apiset.add(sizeof );
}
.
publicobjectinvokeapi (string funcname ) {
开关(funcname ) {
案例打印:
return handlePrintfCall (;
底盘' malloc ' :
返回handlemalloccall (;
底盘尺寸:
返回handlesizeofcall (;
默认:
返回空值;
}
}
私有integercalculatevarsize {
int size=0;
symbol.get arglist ()==null
size=symbol.getByteSize (;
} else {
Symbol head=symbol.getArgList (;
头儿!=null ) {
size=calculatevarsize(head );
head=head.getNextSymbol (;
}
}
eclaratordeclarator=symbol.get declarator (declarator.array );
解释器!=null ) {
size=size * declarator.getelementnum (;
}
return size;
}
calculateVarSize就是解释器对sizeof函数的解释执行,传入参数是sizeof变量对应的Symbol对象,如果getArgsList 返回为空,那表明当前变量不是结构体类型,于是直接通过调用Symbol的getByteSize 函数获取基础数据类型的字节大小。
如果getArgsList返回不是空,那么当前变量类型是结构体,于是把结构体中的每个成员取得,然后递归的调用calculateVarSize去计算每个成员的内存大小。
最后判断下,当前变量是否是数组,如果是数组的话,那么就要把单个变量的大小乘以数组中元素个数,才能得到总的内存大小。
以上代码就是实现sizeof的主逻辑。解释器还有一些地方,代码需要变动,函数调用需要获得传入参数,以前我们的做法是,先获得传入参数的值,然后把这些值存到FunctionArgumentList这个类中,以便解释器执行库函数时容易取得传入参数的值。
sizeof函数需要的不再是变量的值,而是变量对应的Symbol对象,因此我们在解析传入参数时,需要得到参数对应的Symbol对象,由于解析传入参数是在类
ArgsExecutor中实现的,因此需要在该类中,增加获取输入参数Symbol对象的代码:
package backend;
import java.util.ArrayList;
import frontend.CGrammarInitializer;
public class ArgsExecutor extends BaseExecutor {
@Override
public Object Execute(ICodeNode root) {
int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION);
ArrayList argList = new ArrayList();
ArrayList symList = new ArrayList();
ICodeNode child ;
switch (production) {
case CGrammarInitializer.NoCommaExpr_TO_Args:
child = (ICodeNode)executeChild(root, 0);
Object objVal = child.getAttribute(ICodeKey.VALUE);
argList.add(objVal);
objVal = child.getAttribute(ICodeKey.SYMBOL);
symList.add(objVal);
break;
case CGrammarInitializer.NoCommaExpr_Comma_Args_TO_Args:
child = executeChild(root, 0);
objVal = child.getAttribute(ICodeKey.VALUE);
argList.add(objVal);
objVal = child.getAttribute(ICodeKey.SYMBOL);
symList.add(objVal);
child = (ICodeNode)executeChild(root, 1);
ArrayList list = (ArrayList)child.getAttribute(ICodeKey.VALUE);
argList.addAll(list);
list = (ArrayList)child.getAttribute(ICodeKey.SYMBOL);
symList.add(list);
break;
}
root.setAttribute(ICodeKey.VALUE, argList);
root.setAttribute(ICodeKey.SYMBOL, symList);
return root;
}
}
最后,FunctionArgumentList这个类也要做一些改动,原来这个类只提供接口存储参数的值,这次要增加接口,存储参数的Symbol对象,改动如下:
package backend;
import java.util.ArrayList;
import java.util.Collections;
public class FunctionArgumentList {
private static FunctionArgumentList argumentList = null;
private ArrayList funcArgList = new ArrayList();
private ArrayList argSymList = new ArrayList();
public static FunctionArgumentList getFunctionArgumentList() {
if (argumentList == null) {
argumentList = new FunctionArgumentList();
}
return argumentList;
}
public void setFuncArgList(ArrayList list) {
funcArgList = list;
}
public void setFuncArgSymbolList(ArrayList list) {
this.argSymList = list;
}
public ArrayList getFuncArgList(boolean reverse) {
if (reverse == true) {
Collections.reverse(funcArgList);
}
return funcArgList;
}
public ArrayList getFuncArgSymsList(boolean reverse) {
if (reverse == true) {
Collections.reverse(argSymList);
}
return argSymList;
}
private FunctionArgumentList() {}
}
有了以上的代码实现后,我们的解释器就可以解释执行如下C语言代码了:
void main() {
struct TAG {
int p;
char c;
int arr[3];
};
struct TAG myTag[3];
int size ;
size = sizeof(myTag);
printf("The size of struct TAG is : %d", size);
}
上面的代码中,定义了一个结构体叫TAG, 结构体里有三个成员,其中一个是数组,一个结构体变量的内存大小是:4 + 1 + 4*3 = 17. 由于代码中又定义了一个含有三个成员的结构体数组myTag,因此myTag的总大小是51 = 17*3.
我们的解释器解释执行上面代码后得到的结果为:
The size of struct TAG is : 51
也就是我们的解释器确实能够正确的执行sizeof函数。
更详细的代码讲解和调试演示,请参看视频。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号: