当前位置:首页 » 编程软件 » 编译器细节

编译器细节

发布时间: 2022-10-11 08:45:31

① 关于c语言的一些细节问题

语义错误指的是逻辑上的错误,像是你在使用一个变量之前没有声明,不是格式上的错误

语法错误是格式上的错误,比如一个语句最后没有加分号

有的编译器没有语义分析程序,所以你编译时即使语义上有错误,也不会报错,只有当运行时才会错误
所有的编译器都有语法分析程序(理论上是这么说,自己写个编译器当然可以不带)

② 开发一个 C++ 编译器的难度有多大,难点又在哪里

C++的前端是出了名的复杂度和可靠性要求并驾齐驱的软件。

(这两点都比它高一个数量级的大概就只有OS了)

对于这种系统,唯一的办法就是烧钱。

烧钱的作用主要包括:
1.留人;
2.填坑;
3.买买买。

先说留人:复杂度一般是“细节”的代名词。现实中的编译器大多数以递归下降为主,自底向上的归纳推导为辅。这两样在教科书上也就是几页纸的事情。但是现实总是很残酷的,人们总想让语言更加“易用”,这就意味着各种上下文相关的情况都会出现。

对于C++来说,你要判断一个符号是类型或者变量(比如这个符号被用在模板参数中),要看前面的声明/定义。这就是一个上下文相关的推导。然后你就会写大量的if else switch case之类的代码来解决各种各样的可能分支。写它的人当然知道它是做什么的,但是如果这个人离职了,新来一个人,就呆掉了,这写的都是什么煞笔玩意儿。因为它不知道现实中怎样的需求会导致奇形怪状的逻辑。所以人员的稳定,对于这种长周期迭代、逻辑复杂的项目是很重要的。但是人的水平要求高吗?不算高也不算低。总结来说就是:有逻辑,知好歹。技术什么都可以培养,但是态度和基本智商是比较难培养起来的。

至于怎么保证人员稳定?很简单:加薪。

再说填坑:编译器是对正确性要求很高的基础软件。这里的正确性既包括产生的代码的正确性,也包括编译器自身对于各种问题的容忍度和足够丰富的错误提示。容错和错误提示本身也是代码,也有很大的出错几率。所以这些软件,bug少不了。但是作为基础软件,你又不能随便就2+3搞成了2*3,这样还怎么让别人相信爱情。所以要烧很多钱来养一帮debugger。

再说买买买:古人日:我们不用很麻烦很辛苦也可以成佛。既然这么费神我们自己做干什么,不如买别人的吧。于是MS就干脆不自己做了,直接去EDG整了个前端,这样就可以少了不少人年。这就是传统土豪和水果这种新暴发户想的不一样的地方。

传统土豪想的是:我们有这么多钱为什么还要自己解决问题呢?买买买!
水果新贵则是:啊呀,不小心有了这么多钱,我们要不要给自己制造点问题好把这些钱花出去?

③ C语言编译器如何下载

在Code::Blocks官网上下载带mingw组件的Code::Blocks(一定要带有mingw,否则就没有编译器可用),安装,然后就可以用Code::Blocks编写代码和编译运行了。如何使用CB可以自己搜网上教程。

④ 哪位大牛能告诉我,c语言中的'&'这个符号,到底编译器是怎么识别并运用它的

'&'是多功能运算符,至于如何解释该符号,编译器是根据“上下文”来判断的。
由于在C中每个变量必须先声明后使用,比如有声明 :
int x = 3,y = 5,z;
int *p,fun(int &, int &); // 这里显然是引用操作符啦!
对于
z = x & y; // 很明显的,这是逻辑与(and)运算
x &= y; // 这里也是
p = &x; // 这是取址运算,注意,新版的C中没有 =& 双目运算符
-----------------------------------------------------------------------------------------------------------------------
在C++中,有运算符重载函数,比如
aClass operator&(const aClass &x,const aClass &y);,功能是实施类x和y的逻辑与,并返回aClass的实例,这里operator&中的“&”是被重载的运算符,&x,&y中的“&”是引用操作符。
----------------------------------------------------------------------------------------------------------------------
如果你希望了解编译程序的细节,请参考相关书籍。

⑤ 用VS编译和C++源文件的时候,源文件和库文件是如何链接到源文件的具体细节

因为我是学习计算机软件专业的,故可以给你讲一下大概意思,我也不敢保证我讲得都是正确的。个人讲解仅供参考。这个是需要学习《计算机编译原理》这门课程的。而且《计算机编译原理》这门课程在软件专业中几乎是最抽象的、难于理解的。
首先关于 Visual Studio编译器(或者是别的 C/C++编译器)是如何将用户亲自编写的源程序经过若干步骤之后,最终变成计算机可执行的二进制代码程序?这里面经过了如下步骤:
(1)、词法分析/语法分析。也就是说当编译器对用户编写的源程序进行编译时,首先检查你的词法(或者是语法)是否正确,这是第一步(这里以 C 语言为例,假如将定义一个整型变量 n 的语句 int n ; 误写成了:intt n ; 属于语法错误)。如果这一步都没有通过编译器的检查的话,那么绝对不会进入第二步。继续返回编辑状态进行语法检查。这种错误是最容易检查的。
(2)、语义分析。这类错误就要比(1)困难得多。这类错误举例如下(这类错误编译器只是会给出一个警告信息,但是编译器是会放过这类错误的。故需要编程人员具有较丰富的编程经验)
void main( )
{
int num ; /* 定义一个整型变量 num */
scanf("%d", &num ) ; /* 从键盘上输入一个整数 */
if( num == 10 ) /* 在这个语句中,如果将逻辑判断等于号 ==,误写为数值等于 =(即:if( num = 10 )),那么该程序的执行结果始终输出:Correct。因为该逻辑表达式 if( 10 ) 的真值始终为 1。 */
printf( "Correct !\n" ) ; /* 实际上程序的本意是:如果输入的数值等于 10,则输出:Correct ! */
else /* 如果输入的数值不等于 10 的话,则输出:Error ! */
printf( "Error !\n" ) ;
}
(3)、在(1)和(2)的基础上进行中间代码生成(例如:在Linux 系统下面生成的 *.o 文件、或者是在 WINDOWS 系统下面生成的 *.obj 文件),这类文件还不是最终的可执行文件。
在此过程中,会应用到各种符号表,以便处理用户程序中使用的各种常量、变量、以及各种函数,等等。
(4)、在前三个阶段的基础上,最终 VS 编译器再将中间代码(*.obj 文件)和其本身提供的库文件(*.LIB)进行链接,最终产生可执行程序(Linux 系统使用的编译器是:gcc,Linux 系统下面的可执行文件名可以任意,WINDOWS 系统下面的可执行文件名为:*.EXE 文件)。
到此为止,一个用户编写的源程序,经过上面若干步骤之后,最终产生了可执行程序,此时就可以在机器上的相应的操作系统上执行了。

java常见误区与细节有哪些

1、在Java中,没有goto语句。因为大量使用goto语句会降低程序的可读性和可维护性,所以Java语言取消了goto的使用。同时,为了避免程序员自行使用goto所带来的混乱,Java语言仍将goto定义为一个关键字,但是没有定义任何语法,故称为“保留字”。

2 true、false和null在IDE中虽然以不同的颜色显示,但是并不是关键字,而是“字面常量”,就和String类型的abc一样。

3 定义名称时尽量避免使用$,因为编译器在对.java文件进行编译的时候,会将”$”编译成顶层类型与底层类型的连接符。见下例:

  • package

  • com.javastack.

  • Test

  • ;

  • public

  • class

  • Outer$Inner

  • {

  • public

  • static

  • void

  • main(

  • String

  • [] args) {

  • Outer

  • o =

  • new

  • Outer

  • ();

  • Outer

  • .

  • Inner

  • i = o.

  • new

  • Inner

  • ();

  • i.innerPrint();

  • }

  • }

  • class

  • Outer

  • {

  • class

  • Inner

  • {

  • void

  • innerPrint() {

  • System

    .

    out

    .println(

    "Inner Print!"

    );

  • }

  • }

  • }

  • 在编译(javac Test3.java)这段代码的时候,编译器会报以下错误:Test.java:12: 错误: 类重复: com.javastack.Test.Outer.Inner class Inner{ ^

    4Unicode转义字符处理的非常早,在解析程序之前。例如:

  • // char c1 = 'u00a';

  • // char c2 = 'u00d';

  • 在程序中出现这两行代码编译报错。这两个Unicode码分别表示”换行”和”回车”,所以,在编译器编译的时候,代码是这样的:

  • // char c1 = '

  • ';

  • // char c2 = '

  • ';

  • 5 Unicode码使用16位字符编码,在Java中用char类型来表示。现在Unicode已经扩展到一百万个字符,超出16位限制的成为增补字符。所有增补字符都不能用字符常量来表示。

    6 当short,byte,char参加运算时,结果为int型,而非与较高的类型相同。如果变量是byte,short,byte类型,当对其赋予编译时期的常量,而该常量又没有超过变量的取值范围时,编译器就可以进行隐式的收缩转换。这种隐式的收缩转换是安全的,因为该收缩转换只适用于变量的赋值,而不适用于方法调用语句,即不适用于方法调用时的参数传递。(详见java中默认类型转换的小问题)

    7 注意char类型,这是一个无符号类型。因此,char与short或char与byte之间的转换必须显示地使用类型转换。 从byte到char的转换为扩展收缩转换,该转换比较特殊,即先将byte扩展转换到int,然后再收缩到char。

    8 在整型数据间的扩展转换中,如果操作数是char类型(无符号类型),则进行无符号扩展,扩展位为0.如果操作数是byte,short或int(有符号类型),则进行有符号扩展,扩展位为该变量的符号位。

    9 整型数据之间的收缩转换,仅仅是截断并丢弃高位,不做任何其他处理。

    100.1+0.2不等于0.3.System.out.println((double)0.1+(double)0.2);这条语句的输出结果是0.30000000000000004。因为计算机使用二进制来存储数据,而很多小数都不能够准确地使用二进制来表示(事实上,大多数地小数都是近似的),就像使用十进制小数不能准确地表示1/3这样地分数一样。大多数地浮点型,在计算机中只是近似地存储其值,而不像整型那样准确地存储。又例,这是一个死循环:for(float f = 10.1f;f != 11;f+=0.1f){}

    11 float类型可以保留7~8个有效数字,而double类型可以保留15~16个有效数字,因而当int类型或long类型数值多于double或float地有效数字时,该值的一些最低有效位就会丢失,从而造成精度丢失,这时,就会采用IEEE754最近舍入模式,提取与该整型值最接近的浮点值。尽管整型向浮点型的转换属于扩展转换,但当数值很大或很小(绝对值很大)时,就会产生一定的精度丢失。

    12 i+++j如何计算?(这个问题在C/C++)中讨论是没有多大意义的,因为C/C++依赖于实现的硬件结构,不同的环境结果也会不同。不过在Java中,这个结果是固定的,不受其运行的硬件环境与平台的影响) 答:根据贪心规则,前置++优于后置++,结果是(i++)+j

    13i++和++i其实都是先+1,再赋值。++i,没什么好说的;i++,以j=i++;为例在底层的实现是:temp = i;i = i + 1; j = temp; 所以,i=15;i=i++;这个表达式的结果是15.(因为加一之后又执行了一次赋值,从16变回15)

    14 +0与-0在浮点类型变量存储中,符号位是不同的。当-0和+0参与浮点类型的相关运算(例如相除与求余运算)时,可以产生不同的结果。

    15 浮点的相除与求余运算不同与整型的相除与求余运算,当除数为0时,浮点运算不会产生ArithmeticException异常。

    16 String类是非可变类,其对象一旦创建,就不可销毁。String类那些看似修改字符序列的方法实际上都是返回新创建的String对象,而不是修改自身对象。

    17 由于String对象是不可改变的,因此具有线程安全性,可以自由地实现共享。

    18 在String类内部,是使用一个字符数组(char[])来维护字符序列的。String的最大长度也就是字符数组的最大长度,理论上最大长度为int类型的最大值,即2147483647.在实际中,一般可获取的最大值小于理论最大值。

    19 main()方法在表现行为上,与其他方法基本相同,可以重载,由其他方法调用,继承,隐藏,也可以抛出异常,带有类型参数。我们也可以在一个程序中通过反射来调用main方法(或其他方法)。

    20 当两个或多个方法的名称相同,而参数列表不同时,这几个方法就构成了重载。重载方法可以根据参数列表对应的类型与参数的个数来区分,但是,参数的名称、方法的返回类型,方法的异常列表与类型参数不能作为区分重载方法的条件。

    21 究竟选择哪个方法调用,顺序是这样的:

  • 在第一阶段,自动装箱(拆箱)与可变参数不予考虑,搜索对应形参类型可以匹配实参类型并且形参个数与实参个数相同的方法;

  • 如果在步骤一不存在符合条件的方法,在第二阶段,自动装箱与拆箱将会执行。

  • 如果在步骤二中不存在符合条件的方法,在第三阶段,可变参数的方法将会考虑。

  • 如果3个阶段都没有搜索到符合条件的方法,将会产生编译错误。如果如何条件的方法多于一个,将会选择最明确的方法。最明确的方法定义为:如果A方法的形参列表类型对应的都可以赋值给B方法的形参列表类型,则A方法比B方法明确。如果无法选出最明确的方法,则会产生编译错误。

  • 22 重写和隐藏的本质区别是:重写是动态绑定的,根据运行时引用所指向对象的实际类型来决定调用相关类的成员。而隐藏是静态绑定的,根据编译时引用的静态类型来决定调用的相关成员。换句话说,如果子类重写了父类的方法,当父类的引用指向子类对象时,通过父类的引用调用的是子类方法。如果子类隐藏了父类的方法(成员变量),通过父类的引用调用的仍是父类的方法(成员变量)。

    23 构造器是递归调用的,子类的构造器会调用父类的构造器,直到调用Object类的构造器为止。

    24 构造器没有创建对象,构造器是使用new创建对象时由系统调用的,用来初始化类的实例成员。从顺序上说,先是创建对象,然后再调用构造器的。(构造器并没有产生新的对象)

    25 默认的构造器不为空,该构造器会调用父类的无参构造器,并可能执行实例成员变量的初始化。所以,默认的构造器至少调用了父类的构造器,它做的工作还可能更多,包括实例变量声明初始化与实例初始化块,都是在构造器中执行的。

    26 当==或!=运算符的两个操作数的类型一个是基本数据类型,另一个是包装类引用类型时,将引用类型拆箱转换为基本数据类型,然后比较两个基本数据类型的值是否相等。

    27 在Java中,数组也是类,数组声明的引用变量指向数组类型的对象。所有的数组都继承Object类,并且实现了java.lang.Cloneable与java.io.Serializable接口。数组的成员包括变量length(隐式存在)与从Object类继承的成员。Cloneable与Serializable是两个标记的接口,这两个接口中没有显式声明任何成员。

    28 接口是完全抽象的设计,不能实例化。使A用new方式创建的借口类型,实际上是创建了一个匿名类,该匿名类实现了接口类型。

    29 如果两个接口声明了相同的变量x,则当某接口同时继承这两个接口,或者某类同时实现这两个接口时,通过简单名称访问会产生编译错误。

    30 如果两个接口中声明了相同名称的方法m,并且两个方法没有构成重载,则当某接口能够同时继承这两个接口,或者某类能够同时继承这两个接口时,必须存在一种方法签名,使得该签名同时为两个m方法签名的子签名,并且在方法的返回类型上,必须存在一种类型,使得该类型同时为两个m方法返回类型的可替换类型。

⑦ 编译器一般由哪种语言开发

其实我在想为什么汇编语言生成一个简单的编译器后,可以用新生成的编译器再次生成编译器,例如,C语言开发C的编译器呢?
这是一个递归的思想,举个例子一看就明白了
用一个大的模具可以生成一个A模具,A模具可以做出来B模具,依次往下推,最终这个小模具可以做出来小盒子用来装东西。
第一个大模具肯定是手工做出来的第一个模具,但是有了这个大模具后,后面就可以用他自动的生成更多的模具,后面的各种模具加起来又可以造出来更精致的模具,
所以,自动第一个大模具造出来模具的时候,大模具就可以被抛弃了。
我们都知道编译程序通常分为下面五个阶段:
1)词法分析
2)语法分析
3)语义分析与中间代码产生
4)优化
5)目标代码生成
当然最难的一点就是目标代码的生成,这一阶段实现了最终的翻译,就是真正把原码翻译成可以被CPU直接计算的机器码(NativeCode)。

⑧ 如何下载一个C语言编译器具体到细节,不要粘贴的。

你用的不会是Turbo C吧?除了那个,我还没见到不能直接复制的。而且就算是TurboC也可以现在记事本里写程序,然后用Turbo C打开的。

⑨ 本科独立用C语言完成没有优化的C语言编译器属于什么水平

我觉得水平还是很高的,但意义恐怕不大。编译器技术是非常成熟的领域,而且由于应用场景的限
制实时,复杂的算法已经自动出局了,你可选的东西是有限的。编译器可能有很多实现的形
式,虚拟机/解释器/静态编译器 等,也有成熟的开源实现。作为本科生,而非专门研究该分支的学生,应该合理分配自己学习的时间,如果做这个编译器就干
掉了大半年,那计网和OS这些课程该咋办? 

我知道很多人会认为没有做编译器优化特指中段优化,不考虑机器码上的优化比较划水。但编
译器优化是一个很复杂的东西:首先它和你用的IR表示有关而且是强烈耦合,SSA IR基本还
好,有开源代码和文献记载,你想要的都能在网上挖到但这怎么体现你的水平是吧。你
要考虑编译器的性能,尽管编译器的后端优化基本上可以纳入到某种PEabstract interpretation的
范畴中。

要不然你可以通过编写插件的方式白嫖例如visual studio code这类软
件的强大编辑功能,如果你写的不是c compiler,你也可以尽量把语法设计得很像c,这样你又能进一步
白嫖其强大的intellisense code,当然仍然有不少人或者应该说团队达到了这一步,到这里,应该卷死
了99.99%的同行应该毫无问题。

热点内容
百度云下载文件夹 发布:2025-05-18 03:17:33 浏览:674
php云开发 发布:2025-05-18 03:12:41 浏览:447
sql语句显示表 发布:2025-05-18 03:12:30 浏览:690
数据库系统的例子 发布:2025-05-18 03:02:42 浏览:191
数字化储存与编译是什么 发布:2025-05-18 02:56:55 浏览:217
个人网站模板源码 发布:2025-05-18 02:51:17 浏览:490
主服务器ip地址 发布:2025-05-18 02:46:29 浏览:856
电脑配置太低玩不了绝地求生怎么办 发布:2025-05-18 02:38:39 浏览:797
存储过程怎么出错了 发布:2025-05-18 02:37:16 浏览:368
32寸算法 发布:2025-05-18 02:22:14 浏览:744