C编译器剖析_4.4 语义检查_外部声明_类型结构的构建(1)

C编译器剖析_4.4 语义检查_外部声明_结构体和数组的初始化 本节讨论标量类型、数组、结构体和联合体的初始化。 在C语言中,全局或静态变量的初始化应为常量;而局部变量没有此要求。 阅读详情

4.4.1  类型结构的构建

    在这一节中,我们要对C语言的外部声明进行语义检查,按C标准文法,我们需要对“函数定义”和“在函数体外出现的变量与函数声明”进行检查,相关代码主要在declchk.c中。每个C文件在文法中被称为翻译单元TranslationUnit,与之相关的语义检查函数如图4.4.1第1至第15行所示。第7行调用的CheckFunction()用于对函数定义进行语义检查,而第10行调用的CheckGlobalDeclaration()用于对“在函数体外出现的变量和函数声明”进行语义检查。第16至33行是CheckGlobalDeclaration函数的核心代码,第34至49行则为CheckFunction的核心代码。对比这两者的代码,我们可以发现它们都依次调用了对声明说明符DeclarationSpecifiers和声明符Declarator进行语义检查的代码。而被声明的标识符的类型信息正好是分布在DeclarationSpecifiers和Declarator中,通过调用第29或46行的DeriveType函数,会把这两部分的类型信息组合到一起,构造完整的类型信息。例如,对于int arr[4]来说,int是其声明说明符,而arr[4]是声明符,只有把int 和[4]组合到一起,我们才能得到arr的类型信息为int [4]。


图4.4.1 CheckTranslationUnit ()

     如果把函数定义基本上可看成是“函数声明+函数体(即复合语句)”,则函数定义的类型信息主要体现在其函数声明部分。UCC编译器declchk.c中的代码通过对声明的语义检查,建立起C语言的类型系统。具体来说,就是创建我们在“第2.4节 C语言的类型系统”中各个示意图中给出的类型结构。我们在第3章给出了“图3.3.4 声明Declaration所对应的语法树”和“图3.3.7函数定义所对应的语法树”。为阅读方便起见,这里我们再次给出这两个图。

        

图3.3.4 声明Declaration所对应的语法树


图3.3.7函数定义所对应的语法树

    构建类型结构的工作主要由图4.4.1第21行调用的CheckDeclarationSpecifiers,第25行调用的CheckDeclarator和第29行调用的DeriveType函数来完成。再次强调,阅读相关代码时,一定要结合语法图来进行。接下来,让我们先分析一下函数CheckDeclarationSpecifiers。当提到声明说明符时,我们应条件反射般地想到”声明说明符形如static const int”,其中static被称为存储类说明符StorageClassSpecifier,而const被称为类型限定符TypeQualifier,int被称为类型说明符TypeSpecifier。函数CheckDeclarationSpecifiers的主要代码如图4.4.2所示。


图4.4.2 CheckDeclarationSpecifiers()

    图4.4.2第8至15行用来检查存储类说明符,按C标准,一个声明中最多只能有一个StorageClassSpecifier,即只能是extern、auto、static或register中的一个。第16至21行用来检查类型限定符const或volatile。第22至48行则用于对int、double、struct和enum等类型说明符进行检查,我们只给出了主要的代码,省略了一些细节。第27行调用CheckStructOrUnionSpecifier函数,由结构体struct和联合体union对应的语法树来构建类型结构;第30行则调用CheckEnumSpecifier函数来为枚举类型构建类型结构;第32行用于处理用户通过typedefint  INT32定义的类型名INT32,第33行查符号表,从符号表中取出类型名INT32对应的类型信息,对typedef intINT32的语义检查会由declchk.c中的CheckTypedef函数完成;第36至46行为int和double等基本类型构造类型结构,我们只在第40行给出了int作为示范。第50行调用Qualify的函数,把从类型说明符TypeSpecifiers中得到的类型信息ty与类型限定符TypeQualifier结合,从而得到与声明说明符对应的类型结构,存到specs->ty中。其中涉及的函数CheckStructOrUnionSpecifier、CheckEnumSpecifier和CheckTypedef相当复杂,我们会在稍后进行讨论。

    在声明中,另一部分的类型信息由声明符Declarator来指定。在图3.3.4和图3.3.7的语法树中,对于a2、a3和fn来说,经语法分析后,实际上存到这3条链表,为讨论方便,我们不妨称这些链表为Declarator链表。

         astPointerDeclarator(对应*) --->  astDeclarator(对应a2);

         astArrayDeclarator(对应[])  --->  astDeclarator(对应a3);

         astPointerDeclarator(对应*) ---> astFunctionDeclarator(对应())---> astDeclarator(对应fn);

    而函数CheckDeclarator要做的事情就是遍历如上所示的某条链表,收集Declarator链表上各结点的类型信息,最终再通过DeriveType函数,综合由CheckDeclarationSpecifiers和CheckDeclarator这两个函数得来的类型信息,为标识符a2、a3和fn构造出完整的类型结构。为了描述图3.3.4和图3.3.7 astPointerDeclarator结点、astArrayDeclarator结点和astFunctionDeclarator结点中的类型信息,图4.4.3第18至26行中引入了结构体struct typeDerivList。第19行的ctor用于描述类型构造符(即指针、数组和函数),第21行的len用于记录数组的长度(例如int arr[3]中的3),第22行的qual用于记录指针的限定符(例如” int * const  ptr”中的const),第23行的sig用于记录函数形参列表的类型。


图4.4.3 CheckDeclarator()

    函数CheckDeclarator的代码如图4.4.3第1至17行所示,第5至7行用于处理当前结点为数组的情况,第8至10行处理当前结点为函数的情况,第11至13行处理当前结点为指针的情况,而第3至4行则对应如上所示的标识符a2、a3和fn。第27行的CheckArrayDeclarator函数完成了对数组结点的类型信息收集,其结果存于第37行创建的struct  typeDerivList对象中,第38行记录类型构造符为ARRAY_OF(即数组),第39行记录数组的长度,而第41行则指向下一个declarator结点的类型信息,最终构成一个由若干个struct  typeDerivList对象构成的链表。第42行则在当前数组结点astArrayDeclarator中记录标识符的名称。第29行递归地调用CheckDeclarator完成了对Declarator链表的逆序遍历。由于C语言在声明数组时,要求数组大小为常数,第30至35行完成了这个检查。

    图4.4.3第44行的函数CheckPointerDeclarator用于收集Declarator链表中的指针结点所包含的类型信息。例如,对于” int * const ptr;”来说,除了指针*外,我们还要把类型限定符const也包含进来,第49至52完成了对类型限定符的处理,第53至57行则创建struct  typeDerivList对象用来存放类型构造符POINTER_TO和类型限定符,并指向链表中下一个结点收集来的类型信息。由于在C文法的声明说明符中,并未对类型限定符const和类型说明符int的顺序进行限制,即const int和int const是等效的,因此,以下ptr1和ptr2的类型是一样的。类型限定符const是修饰指针本身还是修饰其所指向的对象,要看const是位于运算符*的左侧还是右侧。

         const int * ptr1; // ptr1指向一个const int,但ptr1不是const

         int const * ptr2;// ptr2 指向一个 const int,但ptr2不是const

         const int * const ptr3; // ptr3指向一个const int,且ptr3为const

         int * const ptr4;  // ptr4指向一个int,且ptr4为const

    而图4.4.3第9行调用的CheckFunctionDeclarator函数,则从astFunctionDeclarator结点中收集函数形参列表的类型信息。与之相关的代码,如图4.4.4所示。图4.4.4第7行创建了一个struct  signature对象用于保存函数的形参类型信息,第11行创建了一个向量对象用于保存各形参,最终我们为函数构造的类型结构可参见第2章的“图2.4.9 函数的类型结构”。对于新式风格的函数我们会在第13行通过调用CheckParameterTypeList函数获得其形参类型信息。对于第15行注释中所示的旧式风格函数定义f,此处,我们仅处理(a,b,c)这部分,而对于其后的“int a,b;double c;”我们会在分析CheckFunction函数时再处理,因此在第18行我们只是把各个标识符(例如第15行的a,b,c),通过第18行的AddParameter函数加入到向量funcDec->sig->params中,参数的类型信息我们在第19行中先设为NULL。按C标准的规定,形如“void f(a,b,c);”的旧式声明是非法的,我们会在第21至25行对此进行检查。我们在第26行创建了一个struct typeDerivList对象,用来保存从astFunctionDeclarator结点中收集来的类型信息,第27行设置类型构造符为FUNCTION_RETURN(即函数类型),第28行用于保存形参的类型信息。


图4.4.4 CheckFunctionDeclarator()

     对于以下代码而言,对于f(int a,int b)和g(int c, int d)形参列表的类型收集工作都是由函数CheckFunctionDeclarator完成。处理形参列表时,我们实际上就进入了一个新的作用域,即要调用前面章节中我们介绍过的EnterScope函数来创建新的符号表,但对于函数定义g而言,其局部变量e和形参d位于同一个作用域,相关符号信息存放在相同的符号表中。而对于如下所示的函数h而言,其返回值是一个指向void (void)函数的指针,此时Declarator链表上就会出现两个astFunctionDeclarator结点,分别对应(int a1,int a2)和(void),其中(int a1,int a2)处于一个作用域,而(void)又位于另一个不同的作用域。由于h函数体中的a3和形参a2又处在同一个作用域。因此,当我们调用CheckFunctionDeclarator函数分析完(int a1,int a2)时,要先保存当前作用域的符号表,之后我们会在CheckFunction()中检查h的函数体时,再恢复先前保存的符号表,这样就可以使a1、a2和a3处在相同的符号表中。图4.4.4第49至52行的SaveParameterListTable函数实现了保存当前符号表的功能,而第53至60行的RestoreParameterListTable函数则用于恢复符号表。当我们开始处理形参列表时,我们会在图4.4.4第6行调用EnterParameterList函数,为新的作用域创建相应的符号表,实际的工作由第43行的EnterScope完成;当我们检查完形参列表时,在第34行调用LeaveParemeterList函数,实际的工作由第47行的ExitScope函数完成。对于在形参列表中出现的结构体定义,由于其在外围作用域中是不可见的,所以GCC、Clang和UCC编译器会给出相应警告,例如以下函数k中的struct A。第38行的IsInParameterList函数用来判断我们是否处在形参列表中,以便进行报警。而只有位于函数定义中的形参列表,我们才会调用SaveParameterListTable函数来保存其符号表,以便在检查函数体时进行恢复,如图4.4.4第31至33行所示。

         void f(int a,int b);

         void g(int c, int d){

                   int e;

         }

         void (*h(int a1,int a2))(void){

                   int a3;

                   return NULL;

}

         void k(struct A{int a;} b);

    在图4.4.4第13行中,我们调用了CheckParameterTypeList函数来获取形如(inta,int b)的参数列表中的类型信息,相关代码如图4.4.5第1至13行所示。结合图3.3.7的语法树,我们可以发现第8至11行的while循环实现了对各个形参的检查,第9行调用的函数CheckParameterDeclaration完成了实际的形参检查工作。对于形如int a的形式参数,其类型信息也是分散在声明说明符DeclarationSpecifiers和声明符Declarator中,因此其类型信息收集工作也是由第19、20和27行调用的3个函数来完成。在第21至26行的注释中,我们列出了一些错误的形参声明,为节约篇幅,与此相关的代码我们没有给出。


图4.4.5 CheckParameterTypeList()

    对于形参中的数组和函数类型,按C标准的规定,我们需要将其调整为指针类型,第30行调用的函数AdjustParameter()完成了这个工作,与其相关的代码如图4.4.5第54至57行所示。在C的函数声明的形参中,如果出现不完整的类型IncompleteType,C编译器会给出一个警告,例如以下的函数声明f中的struct Data;而在函数定义的形参列表中出现的IncompleteType,则被视为错误,例如以下的函数定义g中的struct Data。其原因是,在不知道struct Data的类型结构时,我们无法为形参b分配栈空间,而对于函数声明f中的形参a,我们并不需要为之分配栈空间。由于类似的原因,C函数声明中的形参名可以省略,但是函数定义中出现的形参名则不能省略。图4.4.5第36至46行完成了对这两种情况的语义检查,第47行则把形参的类型信息加入到向量funcDec->sig->params中。

    void f(struct Data a);

void g(struct Data b){//此时struct Data还未知,视为IncompleteType

}

struct Data{

         intd;

};

     在图4.4.5第37行,我们调用IsIncompleteType函数用来判断某个类型是否完整,与其

相关的代码在type.c中,如图4.4.6所示。图4.4.6第1至5行的代码用于判断数组类型是否完整,第6至10行用于判断枚举类型是否完整,第11至15行用于检查结构体和联合体类型是否完整。在还未见到结构体、枚举、联合体定义时,我们视这些类型为不完整的,第9行和第14行的标志位complete用于做此标记;而数组大小为0时,我们就将其当作不完整类型的。第16行的IsIncompleteType函数实际上是分别调用第1至15行的这3个函数来判断。


图4.4.6 IsIncompleteType()

    至此,我们讨论了CheckDeclarationSpecifiers和CheckDeclarator这两个函数,为了对经两个函数检查后的结果有个感性认识,我们举以下例子说明。对于arr1和arr2而言,其声明说明符都为int,经CheckDeclarationSpecifiers处理后,我们会得到声明中的部分类型信息T(INT)。

         int * arr1[5];

         int (*arr2)[5];

    而对于*arr1[5]和(*arr2)[5]来说,图4.4.7给出了语法分析后、及经过CheckDeclarator函数所做的语义检查后的类型信息链表。结合声明说明符中所得的类型T(INT),遍历语义检查后的链表,从左向右依次把类型构造符POINTER_TO或ARRAY_OF作用在已有类型上,我们可得到arr1是 array[5] of pointer to int,即arr1是一个长度为5的数组,其数组元素的类型为int *。而arr2是pointer to array[5] of int,即arr2是一个指针,指向int[5]类型的数组。我们在“第1.3节 从文法到分析器”一节中实际上举过类似的例子。


图4.4.7 类型信息链表

    结合CheckDeclarationSpecifiers函数所得到的类型信息,遍历如图4.4.7中所示的由若干个structTypeDerivList对象构成的链表,我们就可构建起“第2.4节 C语言的类型系统”中介绍的类型结构,与此相关的代码如图4.4.8所示。第3至4行用于处理指针类型;C语言中,不允许出现函数的数组,也不允许长度为负数的数组,第5至17行对这些情况进行检查,第18行调用ArrayOf()来构建数组的类型结构;而函数的返回值不可以是数组,也不可以函数类型,第20至27行对此进行检查,第28行调用FuntionReturn()来创建函数的类型结构。


图4.4.8 DeriveType()

    经过DeriveType()函数的类型推导,我们会为int* arr1[5]和int (*arr2)[5]这两个声明分别构建如图4.4.9所示的类型结构。在这小节中,我们重点介绍了CheckDeclarationSpecifiers、CheckDeclarator和DeriveType这3个函数,UCC编译器用这3个函数构建起了类型系统。为了形成整体的概念,在讨论CheckDeclarationSpecifiers函数时,我们有意忽略了为结构体构建类型信息的函数CheckStructOrUnionSpecifier。在下一小节中,我们会对此进行分析。


图4.4.9 类型结构示意图


C编译器剖析_4.1 语义检查_语义检查简介 4.1  语义检查简介     在这一章中,我们需要在语法分析阶段建立的语法树的基础上,进行语义检查。UCC编译器中与此相关的代码主要在ucl\declchk.c,ucl\stmtchk.c和ucl\exprchk.c,分别用于对声明Declaration、语句Statement和表达式Expression进行语义检查SemanticsCheck。其中,最重要的工作就是建立C语言的类型系统,我们 阅读详情

相关推荐

C编译器剖析_4.2 语义检查_表达式的语义检查(4)_函数调用

4.2.4 函数调用的语义检查      在这一小节中,我们来讨论一下函数调用的语义检查,语法上,函数调用对应的表达式属于后缀表达式PostfixExpression,UCC编译器exprchk.c的函数CheckFunctionCall()完成了对函数调用的语义检查,如图4.2.18所示。在阅读这份代码时,需要对语法分析后为函数调用构造的语法树有较好认识,请先参照”图3.1.21后缀运算符对应

SheIsC的专栏 2106

UCC编译器学习笔记16

方案2中,无论a为真还是假,都只需要跳转一次,但是方案1不是,因此UCC编译器选的是第二种中间代码生成方案。这个其实是个模板,就是待判断的表达式为真,则跳转到F2,否则顺序执行块会跳到Next2块。 其实指令的执行,就分为两种,顺序执行,跳转执行(引起了控制流的改变)。顺序执行的最小单元,叫做块bblock。那么生成指令时候,无非就是执行指令(不需要产生有向边),或者跳转到块的指令(由于这个是一个块要到另一个块了,因此同时会产生一条有向边) ...

biao2488890051的博客 773

C编译器剖析_4.2 语义检查_表达式的语义检查(3)_字符串与标识符

4.2.3             在这一小节,我们先来分析一下基本表达式PrimaryExpression的语义检查,由C的标准文法,我们可以知道与PrimaryExpression相关的产生式如下所示,即加了一对小括号的表达式(Expression)在语法上也相当于标志符ID、常量CONST和字符串StringLiteral。 primary-expression:

SheIsC的专栏 2033

c语言编译器UCC学习旅程 总结与感悟

在2013年上本科时候,刚开始学c语言时,程序编译运行。就很神奇,编译过程中,编译器怎么就知道成千上万行代码中哪句代码有问题,能定位到某一行某一列,甚至还能提示你如何修改代码,这是如何做到的呢,也太奇妙了吧!!!尽管有这样的疑惑,但是当时的我实在太菜,也不是计算机专业,于是也没有深究。但是这个疑惑和好奇一直伴随着我。 后面读了计算机研究生,决定解开这个迷惑,寒假期间,于是按照网上推荐,买了经典的编译原理龙书,虎书等等多本书,看了感觉还是很高深,于是去B站看了哈工大老师的编译原理视频,感觉自己好像懂了,但是

biao2488890051的博客 2156

C编译器剖析_4.3 语义检查_语句statement

4.3  对语句Statement的语义检查    在这一节中,我们来分析一下对语句Statement的语义检查,与其相关的代码在stmtchk.c中。图4.3.1第1至16行的数组StmtCheckers,给出了用于对各语句进行语义检查的函数表,第17行的CheckStatement(stmt)函数根据参数stmt的kind域来查这个函数表,由函数指针来调用相应的函数完成语义检查,如图第18行

SheIsC的专栏 1213

C编译器剖析_C类型系统_相容类型Compatible Type

在前文对函数调用进行语义检查时,我们用函数CanAssign()来判断“能否能把实参赋值给形参”,在函数CanAssign中,我们又用宏IsCompatiblePtr来判断两个指针是否相容。而如果指针变量T1 * ptr1和T2 * pt2相容,则意味着类型T1和T2是相容的。UCC编译器中ucl\type.c的函数IsCompatibleType()用于判断类型是否相容,与类型系统相关的数据结构

SheIsC的专栏 2177

C语言学习(十七)---结构体(2)、外部变量声明

主要内容有`结构体(2`),以及对`外部变量`进行仿真,对于学习和掌握结构体,c语言具有一定帮助

weixin_51458778的博客 1719

C语言函数声明与定义:编译器视角的契约本质

函数声明是C语言分离编译模型中的核心契约机制,它在编译阶段提前约定函数接口(参数类型、数量、返回值),使编译器能静态校验调用合法性,避免运行时类型错配;函数定义则提供具体实现,二者共同支撑多文件项目的模块化构建。这种声明/定义分离的设计,直接决定了头文件(.h)与源文件(.c)的职责边界,并成为链接器解析符号依赖、解决undefined reference错误的前提。理解该机制,是掌握C语言工程化开发、VSCode多文件编译配置及Makefile构建演进的基础。

weixin_33738982的博客 1615

【结构体】C语言结构体[外部调用]声明|结构体初始化|做参数

1、现在 FRAM.h 文件中声明一个结构体 struct FRAM_W_ADDR { char end[3]; char start[3]; }; extern struct FRAM_W_ADDR DATA_REC_ADDR; 2、在FARM.c文件中定义一个结构体变量 struct FRAM_W_ADDRDATA_REC_ADD...

我的笔记本 1万+

C编译器剖析_6.1 汇编代码生成_简介

6.1 汇编代码生成简介     历经词法分析、语法分析、语义检查和中间代码生成阶段,我们终于来到了“目标代码生成阶段”,由于UCC编译器的目标代码即为32位x86汇编代码,因此我们就把本章称为“汇编代码生成”。UCC编译器中的大部分源代码都适用于Windows和Linux平台,但Windows平台上缺省的汇编器支持Intel风格的x86汇编代码,而Linux平台默认的汇编器则采用AT&T风格的

SheIsC的专栏 2769

深入浅出C语言编译器源码剖析

本文还有配套的精品资源,点击获取 简介:编译器是将高级编程语言转换为机器语言的关键工具,本资源提供的“简单编译器”源码用C语言编写,涵盖编译器设计的各个主要阶段,如词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成。通过学习和调试这些源码,开发者能够深入理解编译原理和编程语言的内部工作,提升C语言编程技能,掌握复杂数据结构和算法的应用。建议学习者先了解编译原...

weixin_42451850的博客 1499

ucc编译器(入门)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 要说开源编译器,一般大家想到的都是gcc。但是现在gcc的代码量太大了,不太适合学习。代码量比较适合学习的编译器,如果google一下,基本上就剩下lcc和ucc这两个项目。其中,lcc支持多个cpu,而ucc目前只支持x86、且很长时间没有人维护了。但是从代码的可阅读性来说,我...

嵌入式-老费,一个分享专业嵌入式知识的blog 6198

C语言编译器源码项目:编译原理学习与实践

词法分析是编译器的第一阶段,主要任务是将源代码的字符序列转换为一系列的词法单元(也称为记号或tokens)。词法单元是具有特定意义的最小单元,它代表了编程语言的原子符号,例如关键字、标识符、字面量、运算符和分隔符。词法单元可以分为以下几类:关键字(Keywords):如ifelsewhilefor等,这些是编程语言的保留字,具有特定的语法意义。标识符(Identifiers):用于变量名、函数名等,由字母、数字和下划线组成,但第一个字符不能是数字。

weixin_31961675的博客 1043

C编译器剖析_4.4 语义检查_外部声明_类型结构的构建(2)

在这一小节中,我们将对形如第3章图3.3.17所示的结构体语法树进行语义检查,从而构建结构体的类型结构。          图3.3.17 ParseStructOrUnionSpecifier()构建的语法树     我们在第2章中给出了以下结构体struct Data对应的类型结构,如图2.4.4所示。为了阅读方便,我们重新给出这2幅图,由图示我们也能较清楚地预览本节的起点和终点。

SheIsC的专栏 1494

ucc-c-compiler:用C实现的C编译器

用C语言编写的AC编译器 依存关系 C标准库 as (使用gnu-as和darwin-as [现在clang-as]进行了测试,但是任何都可以) ld (任何都可以) 使用C89编译器来构建该编译器 特征 编译器实现C89,C99和C11(可通过-std=c89/c99/c11 )。 完全支持系统库,包括与va_list结构的ABI兼容性。 以下列出了一些主要的新增功能: 增加的功能: Microsoft / Plan 9结构扩展 Lambda / Objective-C样式块 函数的尾随返回类型 命名空间检查 标准支持(并非详尽无遗): C89,C99 C11 _Bool , _Noreturn , _Alignof , _Alignas _Generic , _Static_assert 功能支持: 浮点类型 无符号整数 char , short , long和(仅在6

中文编译器 ucc

UCC是由清华学生开发的一块简单c语言编译器,结构清晰,方便学习!

C编译器剖析_4.2 语义检查_表达式的语义检查(2)_数组索引

在这一小节中,我们准备分析一下exprchk.c中的函数CheckPostfixExpression,此函数用于对后缀表达式进行语义检查。后缀表达式所对应的语法树请参照第3章的图3.1.21,这里不再重复。我们先从数组索引入手,图4.2.4给出了hello.c经UCC编译后生成的语法树hello.ast,中间代码hello.uil及最终生成的汇编代码hello.s的主要部分。 图4.

SheIsC的专栏 1298

信奥编程启蒙 3068:【例13.3】时间差

现在给出一个时间,单位是秒,要求按照电子表格式输出,输出保证不会超过 2424 小时。3067:【例13.2】 电子表。把时间转化成电子表格式输出。电子表上的时间显示方法形如。

900

C2编程语言编译器内部机制揭秘:从源码到二进制文件的完整流程

C2编程语言编译器(c2c)是一个用C2语言编写的自举编译器,它将C2源代码转换为可执行文件或库。作为C语言的演进版本,C2编译器通过现代化的架构设计实现了高效的编译流程。本文将深入解析C2编译器的工作原理,从词法分析到代码生成,为您揭开编译器内部的神秘面纱。 ## 🔍 C2编译器架构概览 C2编译器采用模块化设计,主要分为以下几个核心组件: - **前端处理层**:负责命令行参数解析和构

gitblog_00567的博客 904

Flutter 直接调用so动态库,或调用C/C++源文件内函数

我把 .so 文件尝试放过好几个目录/lib/,最终都会报这个错误,现在才知道库文件需要放在你根本想象不到的地方!!!尝试了一些方法还是不行后我放弃了 ffi,想着用 Flutter 的 MethodChannel 桥接 android/ios 原生,再让原生去调 native 层。一顿操作把Android端的搞定了,当然中间涉及到 Kotlin和C++ 层的数据类型映射的痛苦,而且业务函数也不像一个 helloworld 那么简单,别提有多痛苦了。

开发以大橘为重 8350

别再死磕公式了!用Python+NumPy手把手实现机器人逆运动学数值求解(附避坑指南)

本文详细介绍了如何使用Python和NumPy实现机器人逆运动学(Inverse Kinematics, IK)的数值求解方法,包括牛顿-拉弗森法、雅可比矩阵计算及奇异位形处理。通过实战代码和避坑指南,帮助开发者快速掌握现代机器人学中的核心算法,提升工程实践能力。

weixin_30687051的博客 127
上一篇: C编译器剖析_4.3 语义检查_语句statement
下一篇: C编译器剖析_4.4 语义检查_外部声明_类型结构的构建(2)
SheIsC
博客等级 码龄12年 224粉丝 64原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值