A. 编译器的组成及各部分的功能及作用
1. 词法分析 词法分析器根据词法规则识别出源程序中的各个记号(token),每个记号代表一类单词(lexeme)。源程序中常见的记号可以归为几大类:关键字、标识符、字面量和特殊符号。词法分析器的输入是源程序,输出是识别的记号流。词法分析器的任务是把源文件的字符流转换成记号流。本质上它查看连续的字符然后把它们识别为“单词”。 2. 语法分析 语法分析器根据语法规则识别出记号流中的结构(短语、句子),并构造一棵能够正确反映该结构的语法树。 3. 语义分析 语义分析器根据语义规则对语法树中的语法单元进行静态语义检查,如果类型检查和转换等,其目的在于保证语法正确的结构在语义上也是合法的。 4. 中间代码生成 中间代码生成器根据语义分析器的输出生成中间代码。中间代码可以有若干种形式,它们的共同特征是与具体机器无关。最常用的一种中间代码是三地址码,它的一种实现方式是四元式。三地址码的优点是便于阅读、便于优化。 5. 中间代码优化 优化是编译器的一个重要组成部分,由于编译器将源程序翻译成中间代码的工作是机械的、按固定模式进行的,因此,生成的中间代码往往在时间和空间上有很大浪费。当需要生成高效目标代码时,就必须进行优化。 6. 目标代码生成 目标代码生成是编译器的最后一个阶段。在生成目标代码时要考虑以下几个问题:计算机的系统结构、指令系统、寄存器的分配以及内存的组织等。编译器生成的目标程序代码可以有多种形式:汇编语言、可重定位二进制代码、内存形式。 7 符号表管理 符号表的作用是记录源程序中符号的必要信息,并加以合理组织,从而在编译器的各个阶段能对它们进行快速、准确的查找和操作。符号表中的某些内容甚至要保留到程序的运行阶段。 8 出错处理用户编写的源程序中往往会有一些错误,可分为静态错误和动态错误两类。所谓动态错误,是指源程序中的逻辑错误,它们发生在程序运行的时候,也被称作动态语义错误,如变量取值为零时作为除数,数组元素引用时下标出界等。静态错误又可分为语法错误和静态语义错误。语法错误是指有关语言结构上的错误,如单词拼写错、表达式中缺少操作数、begin和end不匹配等。静态语义错误是指分析源程序时可以发现的语言意义上的错误,如加法的两个操作数中一个是整型变量名,而另一个是数组名等。
B. 华为p30怎么样 值不值得买
搭载了海思麒麟980处理器,对比骁龙855芯片也是丝毫不逊色的,搭配的8GB+128GB的超大存储组合绝对能够满足众人在日常的生活需求,平时玩手游的用户反映流畅度还是较为给力的,这一点毋庸置疑。
这款系列还是值得购买的,虽然在价格上颇贵,但是各项的功能都算顶尖级别,这样奢华而精美的旗舰机型自然在价格上也是不菲,它的性能、续航能力让不少人为之赞美
C. 编译时分配内存和运行时分配内存
编译其实只是一个扫描过程,进行词法语法检查,代码优化而已,编译程序越好,程序运行的时候越高效。
我想你说的“编译时分配内存”是指“编译时赋初值”,它只是形成一个文本,检查无错误,并没有分配内存空间。
当你运行时,系统才把程序导入内存。一个进程(即运行中的程序)在主要包括以下五个分区:
栈、堆、bss、data、code
代码(编译后的二进制代码)放在code区,代码中生成的各种变量、常量按不同类型分别存放在其它四个区。系统依照代码顺序执行,然后依照代码方案改变或调用数据,这就是一个程序的运行过程。
D. 编译器能够完成的工作是
1. 词法分析词法分析器根据词法规则识别出源程序中的各个记号(token),每个记号代表一类单词(lexeme)。源程序中常见的记号可以归为几大类:关键字、标识符、字面量和特殊符号。词法分析器的输入是源程序,输出是识别的记号流。词法分析器的任务是把源文件的字符流转换成记号流。本质上它查看连续的字符然后把它们识别为“单词”。
2. 语法分析语法分析器根据语法规则识别出记号流中的结构(短语、句子),并构造一棵能够正确反映该结构的语法树。
3. 语义分析语义分析器根据语义规则对语法树中的语法单元进行静态语义检查,如果类型检查和转换等,其目的在于保证语法正确的结构在语义上也是合法的。
4. 中间代码生成中间代码生成器根据语义分析器的输出生成中间代码。中间代码可以有若干种形式,它们的共同特征是与具体机器无关。最常用的一种中间代码是三地址码,它的一种实现方式是四元式。三地址码的优点是便于阅读、便于优化。
5. 中间代码优化
优化是编译器的一个重要组成部分,由于编译器将源程序翻译成中间代码的工作是机械的、按固定模式进行的,因此,生成的中间代码往往在时间和空间上有很大浪费。当需要生成高效目标代码时,就必须进行优化。
6. 目标代码生成
目标代码生成是编译器的最后一个阶段。在生成目标代码时要考虑以下几个问题:计算机的系统结构、指令系统、寄存器的分配以及内存的组织等。编译器生成的目标程序代码可以有多种形式:汇编语言、可重定位二进制代码、内存形式。
7 符号表管理
符号表的作用是记录源程序中符号的必要信息,并加以合理组织,从而在编译器的各个阶段能对它们进行快速、准确的查找和操作。符号表中的某些内容甚至要保留到程序的运行阶段。
8 出错处理用户编写的源程序中往往会有一些错误,可分为静态错误和动态错误两类。所谓动态错误,是指源程序中的逻辑错误,它们发生在程序运行的时候,也被称作动态语义错误,如变量取值为零时作为除数,数组元素引用时下标出界等。静态错误又可分为语法错误和静态语义错误。语法错误是指有关语言结构上的错误,如单词拼写错、表达式中缺少操作数、begin和end不匹配等。静态语义错误是指分析源程序时可以发现的语言意义上的错误,如加法的两个操作数中一个是整型变量名,而另一个是数组名等。
E. 编译器做什么工作
1. 词法分析 词法分析器根据词法规则识别出源程序中的各个记号(token),每个记号代表一类单词(lexeme)。源程序中常见的记号可以归为几大类:关键字、标识符、字面量和特殊符号。词法分析器的输入是源程序,输出是识别的记号流。词法分析器的任务是把源文件的字符流转换成记号流。本质上它查看连续的字符然后把它们识别为“单词”。 2. 语法分析 语法分析器根据语法规则识别出记号流中的结构(短语、句子),并构造一棵能够正确反映该结构的语法树。 3. 语义分析 语义分析器根据语义规则对语法树中的语法单元进行静态语义检查,如果类型检查和转换等,其目的在于保证语法正确的结构在语义上也是合法的。 4. 中间代码生成 中间代码生成器根据语义分析器的输出生成中间代码。中间代码可以有若干种形式,它们的共同特征是与具体机器无关。最常用的一种中间代码是三地址码,它的一种实现方式是四元式。三地址码的优点是便于阅读、便于优化。 5. 中间代码优化 优化是编译器的一个重要组成部分,由于编译器将源程序翻译成中间代码的工作是机械的、按固定模式进行的,因此,生成的中间代码往往在时间和空间上有很大浪费。当需要生成高效目标代码时,就必须进行优化。 6. 目标代码生成 目标代码生成是编译器的最后一个阶段。在生成目标代码时要考虑以下几个问题:计算机的系统结构、指令系统、寄存器的分配以及内存的组织等。编译器生成的目标程序代码可以有多种形式:汇编语言、可重定位二进制代码、内存形式。 7 符号表管理 符号表的作用是记录源程序中符号的必要信息,并加以合理组织,从而在编译器的各个阶段能对它们进行快速、准确的查找和操作。符号表中的某些内容甚至要保留到程序的运行阶段。 8 出错处理用户编写的源程序中往往会有一些错误,可分为静态错误和动态错误两类。所谓动态错误,是指源程序中的逻辑错误,它们发生在程序运行的时候,也被称作动态语义错误,如变量取值为零时作为除数,数组元素引用时下标出界等。静态错误又可分为语法错误和静态语义错误。语法错误是指有关语言结构上的错误,如单词拼写错、表达式中缺少操作数、begin和end不匹配等。静态语义错误是指分析源程序时可以发现的语言意义上的错误,如加法的两个操作数中一个是整型变量名,而另一个是数组名等。
F. 通过编译器对程序优化来改进cache性能的方法有哪几种
你的程序可能太短,看不出区别来,你比对一下她们生成的汇编码就知道了
CPU 缓存是为了提高程序运行的性能,CPU 在很多处理上内部架构做了很多调整,比如 CPU 高速缓存,大家都知道因为硬盘很慢,可以通过缓存把数据加载到内存里面,提高访问速度,而 CPU 处理也有这个机制,尽可能把处理器访问主内存时间开销放在 CPU 高速缓存上面,CPU 访问速度相比内存访问速度又要快好多倍,这就是目前大多数处理器都会去利用的机制,利用处理器的缓存以提高性能。
就算优化带来的效果非常有限,但是经过长年累月的持续优化,效果也是非常明显的,比如当年的Chrome浏览器就是靠打开网页非常快从而打败微软系统自带的IE浏览器。电脑手机等硬件的性能是有限的,不同的算法会产生不同的效率,今天我们就简单说一个选择问题,开发程序时是节省内存还是节省计算量。
G. “编译器”如何设置内存区域
不是.
编译好后的exe文件并非只有代码部分,还有其他的部分如数据部分以及其他.其中包括诸如内存如何分配,堆栈如何处理等等的描述.而这些描述就是编译器写进exe文件里的.
如果想知道的详细些,可以简单的看一下关于PE结构的描述~~
H. linux共享内存会不会被编译器优化
利用共享内存(Share Memory)可以让我们在任意两个进程之间传递数据,而且也是相对容易实现的一种方法,在正常情
况下,一个进程所使用的内存是不允许其它进程访问的,但是通过共享内存可以实现数据的共享。
使用共享内存用到的API函数有:
# include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
key_t ftok(const char *pathname, int proj_id)
其中pathname为当前进程的程序名,第二个参数为进程所开辟的第几个共享内存,返回一个key_t值,用于共享内存的获取
使用。
int shmget(key_t shmkey, int shmsiz, int flag)
void *shmat(int shmid, char *shmaddr, int shmflag)
int shmdt(char *shmaddr)
shmget是用来创建或者指向一块共享内存的函数,其中shmkey是共享内存的标识符,如果父子关系的进程间通信的话,这
个标识用IPC_PRIVATE替代,如果没有任何关系,可以使用ftok计算出来;Shmsiz是这块内存的大小;flag是这块内存的模
式和权限标识(IPC_CREAT, IPC_ALLOC, IPC_EXCL,0666权限)。函数成功返回共享内存的shmid,否则返回-1表示失败。
Shmat用来允许进程访问共享内存的函数,其中shmid是共享内存的ID,shmaddr是共享内存起始位置,shmflag是本进程对
内存操作模式,SHM_RDONLY是只读模式,成功返回共享内存的起始位置。
Shmdt与shmat相反,是用来禁止进程访问一块共享内存的函数。
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
其中shmid是共享内存的ID,cmd是控制命令(IPC_STAT, IPC_SET, IPC_RMID),struct shmid_ds *buf是一个结构体指针
,如果要改变共享内存的状态,使用它来指定,获得状态就存放在这个结构体中。
上述API函数的详细说明请使用man(男人)查看:
# man shmget | shmat | shmdt | shmctl
下面使用一个简单的例子说明如何使用共享内存。
// sharememcut.c 拷贝用户输入的字符串到共享内存中
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
key_t shmkey;
int shmid, in_tmp;
char *head, *pos, in_data[4096], *in_ptr;
// Create share memory KEY
shmkey = ftok("sharememcut", 1);
// Get the share memory ID
shmid = shmget(shmkey, sizeof(in_data), IPC_CREAT | 0666);
// Allow the process to access share memory, and get the address
head = pos = shmat(shmid, 0, 0);
in_ptr = in_data;
// Receive the character from stdin, 'q' to quit
while ((in_tmp = getchar()) != 'q') {
*in_ptr = in_tmp;
in_ptr++;
}
*in_ptr = '\0';
in_ptr = in_data;
// Cut the data into share memory
while (*in_ptr != '\0') {
*pos++ = *in_ptr++;
}
// Prohabit the process to access share memory
shmdt(head);
return 0;
}
# gcc -o sharememcut sharememcut.c 编译sharememcut.c
# ./sharememcut 执行sharememcut创建共享内存
…
# ipcs -m | grep 4096 查看创建的共享内存
0x01068288 348848145 root 666 4096 0
// sharemempaste.c 将共享内存的内容显示在屏幕上并且删除共享内存
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
key_t shmkey;
int shmid;
char *head, *pos, out_data[4096], *out_ptr;
// Create share memory KEY
shmkey = ftok("sharememcut", 1);
// Get the share memory ID
shmid = shmget(shmkey, sizeof(out_data), 0666);
if (shmid == -1)
return -1;
// Allow the process to access share memory, and get the address
head = pos = shmat(shmid, 0, 0);
out_ptr = out_data;
// Get the data from share memory
while (*pos != '\0') {
*out_ptr++ = *pos++;
}
*out_ptr = '\0';
// Output the data into stdout
printf("%s\n", out_data);
fflush(stdout);
// Prohabit the process to access share memory
shmdt(head);
// Delete the share memory
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
# gcc -o sharemempaste sharemempaste.c 编译sharemempaste.c
# ./sharemempaste 执行程序显示共享内存的内容
I. 在不考虑编译器优化等因素下,下面那个运算比较快
下面是在编译器不优化的情况下的代码反汇编后的结果
14: if (a > b)//我们看到,只有3条指令,
0040104D mov edx,dword ptr [ebp-4] //将a放入寄存器EDX
00401050 cmp edx,dword ptr [ebp-8] //比较EDX和b的大小
00401053 jle main+3Ch (0040105c) //如果a<b跳转到0040105c
15: {
16: return 1;
00401055 mov eax,1
0040105A jmp main+4Fh (0040106f)
17: }
18: if (a - b > 0)//我们看到有4条指令
0040105C mov eax,dword ptr [ebp-4]//将a放入EAX
0040105F sub eax,dword ptr [ebp-8]//a-b的差放入EAX
00401062 test eax,eax//比较EAX是否为0
00401064 jle main+4Dh (0040106d)//条件跳转
19: {
20: return 2;
00401066 mov eax,2
0040106B jmp main+4Fh (0040106f)
21: }
所以我认为if (a > b) {....}
效率更高
J. 编译器在编译的时候做了什么给申明的变量分配内存
第一是将java文件编译成字节码文件 就是class文件 给jvm执行
第二就是分配常量池 就是给你代码里面的变量和方法分配空间