⑴ C语言程序编译后产生哪些类型的文件这些文件的作用是什么
1、以GCC编译器为例,可以分为四步。
第一步是预处理,包括语法检查等工作。
gcc
-P
abc.c
第二步由源程序生产汇编语言代码。
gcc
-S
abc.c
会生成abc.s文件,这个文件里就是汇编代码。
第三步编译器生成目标代码,一个源文件生成一个目标代码。
gcc
-c
abc.c
会生成abc.o
第四步连接器从目标代码生成可执行文件。
gcc
abc.o
2、目标代码包括机器码和符号表(函数及变量名)。连接器的主要作用是通过符号表在库文件和其他模块中找到在目标代码中引入或未定义的符号(函数及变量名),将几个目标代码合成可执行文件。
⑵ 什么是符号表 符号表有哪些重要作用
符号表是一种用于语言翻译器(例如编译器和解释器)中的数据结构。在符号表中,程序源代码中的每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型、作用域以及内存地址。
符号表的作用:符号表在编译程序工作的过程中需要不断收集、记录和使用源程序中一些语法符号的类型和特征等相关信息。这些信息一般以表格形式存储于系统中。
如常数表、变量名表、数组名表、过程名表、标号表等等,统称为符号表。对于符号表组织、构造和管理方法的好坏会直接影响编译系统的运行效率。
(2)编译程序时在符号表中查找符号扩展阅读
编译程序按名字的不同种属分别使用许多符号表,如常熟表。变量名表过程名表函数入口名表。符号表的关键字域(段)就是符号名称等长关键字域(段)符号表。
不等长关键字段符号表,采用关键字词的索引结构。关键字在符号表的查找中相当重要hash函数构造方法就是以取关键字的值不同区分,如直接寻址伐数字分析法折叠法。
⑶ 编译原理对符号表进行操作有哪些
//----------------------------符号表---------------------------------------
//预定义
struct snode;
struct stable;
//符号表结点
struct snode
{
string text; //符号名称
string type; //符号类型
union {int ival;double rval;}value; //值------------
int offset; //偏移量
snode *nextn; //指向下一个节点
stable *header; //指向下属符号表的表头
};
//符号表表头
struct stable
{
stable *previous; //指向先前创建的符号表表头
snode *firstnode; //指向第一个结点
stable *ifnoelements;//如果此表为空,则用它指向下一个表头
};
//当前表头
stable *currtab;
//建立新表,返回表头指针
//参数:当前的节点的表头
stable *mktable(stable *previous)
{
stable *newtable =new stable;
newtable->previous=previous;
newtable->ifnoelements=0;
newtable->firstnode=0;
if(previous->firstnode==0)
{
previous->ifnoelements=newtable;
}
else
{
snode* ininode=previous->firstnode;
while(ininode->nextn!=0)
{
ininode=ininode->nextn;
}
ininode->header=newtable;
}
currtab=newtable;
return newtable;
}
//在node指向的符号表中为text建立一个新表项,返回新建立的结点
//参数:node为当前的节点的表头,text名称,type类型,offset偏移
snode *enter(stable *table,string text,string type,int offset,double value)
{
//创建节点
snode *newnode = new snode;
newnode->text=text;
newnode->type=type;
newnode->offset=offset;
newnode->nextn=0;
newnode->header=0;
if(type=="int")
{
newnode->value.ival=value;
}
else if(type=="real")
{
newnode->value.rval=value;
}
//判断此表是否无元素
if(currtab->firstnode==0)
{
currtab->firstnode=newnode;
currtab->ifnoelements=0;
}
else
{
snode* addnode=currtab->firstnode;
while(addnode->nextn!=0)
{
addnode=addnode->nextn;
}
addnode->nextn=newnode;
}
return newnode;
}
//初始化符号表,返回表头节点
void inittab()
{
stable *initable = new stable;
initable->firstnode=0;
initable->previous=0;
initable->ifnoelements=0;
currtab=initable;
}
//查找指针,表示结果
snode *searchresult;
//查找变量,返回指向该变量的指针
//查找变量,返回指向该变量的指针
snode* search(string name)
{
//检查表是否空
bool isempty=true;
stable* checktab=currtab;
if(checktab->firstnode!=0)
{isempty=false;}
while(checktab->previous!=0)
{
if(checktab->firstnode!=0)
{isempty=false;}
checktab=checktab->previous;
}
if(checktab->firstnode!=0)
{isempty=false;}
if(isempty)
{
return 0;
}
snode* lastnode;
if(currtab->firstnode==0)
{
//移到非空的表头
stable* notnullhead=currtab;
while(notnullhead->firstnode==0)
{
notnullhead=notnullhead->previous;
}
snode* tmpnode=notnullhead->firstnode;
//移到最后的元素
while(tmpnode->nextn!=0)
{
tmpnode=tmpnode->nextn;
}
lastnode=tmpnode;
}
else
{
lastnode=currtab->firstnode;
while(lastnode->nextn!=0)
{
lastnode=lastnode->nextn;
}
}
//移到表头
stable* fronttab=currtab;
while(fronttab->previous!=0)
{
fronttab=fronttab->previous;
}
snode* nownode=0;
while(nownode!=lastnode)
{
while(fronttab->ifnoelements!=0)
{
fronttab=fronttab->ifnoelements;
}
nownode=fronttab->firstnode;
while(nownode->nextn!=0)
{
if(nownode->text==name)
{
searchresult=nownode;
return searchresult;
}
nownode=nownode->nextn;
}
if(nownode->text==name)
{
searchresult=nownode;
return searchresult;
}
fronttab=nownode->header;
}
if(nownode->text==name)
{
searchresult=nownode;
return searchresult;
}
return 0;
}
//消毁符号表
void delNode()
{
//more codes here......
}
⑷ 编程常见的符号表和结构查找方法
<code>weeds@weeds-ThinkPad-T440p:~/Documents/encrypchip$ objmp -t main.o
main.o: file format elf32-little
SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .mdebug.abi32 00000000 .mdebug.abi32
00000000 l d .rodata 00000000 .rodata
00000000 l d .reginfo 00000000 .reginfo
00000000 l d .pdr 00000000 .pdr
00000000 l d .comment 00000000 .comment
00000000 l d .gnu.attributes 00000000 .gnu.attributes
00000000 g F .text 000000dc demo1
00000000 *UND* 00000000 GetIdChipSerialNo
00000000 *UND* 00000000 printf
00000000 *UND* 00000000 putchar
000000dc g F .text 0000027c demo2
00000000 *UND* 00000000 _alpu_rand
00000000 *UND* 00000000 gettimeofday
00000000 *UND* 00000000 alpuc_process
00000000 *UND* 00000000 puts
00000358 g F .text 0000003c main
</code>
readelf -h xxx.o
<code>weeds@weeds-ThinkPad-T440p:~/Documents/encrypchip$ readelf -h main.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1624 (bytes into file)
Flags: 0x1005, noreorder, cpic, o32, mips1
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 15
Section header string table index: 12
</code>
readelf -s main.o
<code>weeds@weeds-ThinkPad-T440p:~/Documents/encrypchip$ readelf -s main.o
Symbol table '.symtab' contains 21 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS main.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 8
6: 00000000 0 SECTION LOCAL DEFAULT 9
7: 00000000 0 SECTION LOCAL DEFAULT 5
8: 00000000 0 SECTION LOCAL DEFAULT 6
9: 00000000 0 SECTION LOCAL DEFAULT 10
10: 00000000 0 SECTION LOCAL DEFAULT 11
11: 00000000 220 FUNC GLOBAL DEFAULT 1 demo1
12: 00000000 0 NOTYPE GLOBAL DEFAULT UND GetIdChipSerialNo
13: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf
14: 00000000 0 NOTYPE GLOBAL DEFAULT UND putchar
15: 000000dc 636 FUNC GLOBAL DEFAULT 1 demo2
16: 00000000 0 NOTYPE GLOBAL DEFAULT UND _alpu_rand
17: 00000000 0 NOTYPE GLOBAL DEFAULT UND gettimeofday
18: 00000000 0 NOTYPE GLOBAL DEFAULT UND alpuc_process
19: 00000000 0 NOTYPE GLOBAL DEFAULT UND puts
20: 00000358 60 FUNC GLOBAL DEFAULT 1 main
</code>
nm xxx.o 对比g++编译程序生成的符号表与gcc生成的不同点。
?
⑸ 关于C语言指针,打破砂锅问到底
程序编译阶段,编译器会生成一个符号表,里面记录的是每一个变量的相关信息,比如,类型,分配的地址等等。
编译的时候会查询符号表来编译,举个例子:
int *p;
假如符号表中记录的 p 所在的地址是1234;
p=&a;
当遇到这句的时候,编译器会查找符号表,找到符号a的信息,把它的地址记录下来(假设为4567),接着找到p的地址为1234,然后把上面那句话翻译为:*(int *)(1234)=4567;(意在说明问题,实际的产生的并非这样的代码,但原理是类似的)
,(int *)&&p,(int *)&&&p这些语句都是无效的。
想要深入了解,可以看看编译原理的符号表内容
希望对你有帮助
⑹ 编译器的组成及各部分的功能及作用
1. 词法分析 词法分析器根据词法规则识别出源程序中的各个记号(token),每个记号代表一类单词(lexeme)。源程序中常见的记号可以归为几大类:关键字、标识符、字面量和特殊符号。词法分析器的输入是源程序,输出是识别的记号流。词法分析器的任务是把源文件的字符流转换成记号流。本质上它查看连续的字符然后把它们识别为“单词”。 2. 语法分析 语法分析器根据语法规则识别出记号流中的结构(短语、句子),并构造一棵能够正确反映该结构的语法树。 3. 语义分析 语义分析器根据语义规则对语法树中的语法单元进行静态语义检查,如果类型检查和转换等,其目的在于保证语法正确的结构在语义上也是合法的。 4. 中间代码生成 中间代码生成器根据语义分析器的输出生成中间代码。中间代码可以有若干种形式,它们的共同特征是与具体机器无关。最常用的一种中间代码是三地址码,它的一种实现方式是四元式。三地址码的优点是便于阅读、便于优化。 5. 中间代码优化 优化是编译器的一个重要组成部分,由于编译器将源程序翻译成中间代码的工作是机械的、按固定模式进行的,因此,生成的中间代码往往在时间和空间上有很大浪费。当需要生成高效目标代码时,就必须进行优化。 6. 目标代码生成 目标代码生成是编译器的最后一个阶段。在生成目标代码时要考虑以下几个问题:计算机的系统结构、指令系统、寄存器的分配以及内存的组织等。编译器生成的目标程序代码可以有多种形式:汇编语言、可重定位二进制代码、内存形式。 7 符号表管理 符号表的作用是记录源程序中符号的必要信息,并加以合理组织,从而在编译器的各个阶段能对它们进行快速、准确的查找和操作。符号表中的某些内容甚至要保留到程序的运行阶段。 8 出错处理用户编写的源程序中往往会有一些错误,可分为静态错误和动态错误两类。所谓动态错误,是指源程序中的逻辑错误,它们发生在程序运行的时候,也被称作动态语义错误,如变量取值为零时作为除数,数组元素引用时下标出界等。静态错误又可分为语法错误和静态语义错误。语法错误是指有关语言结构上的错误,如单词拼写错、表达式中缺少操作数、begin和end不匹配等。静态语义错误是指分析源程序时可以发现的语言意义上的错误,如加法的两个操作数中一个是整型变量名,而另一个是数组名等。
⑺ 简述一下编译器和链接器的作用
1、编译器:
编译器对源文件进行编译,就是把源文件中的文本形式存在的源代码翻译成机器语言形式的目标文件的过程,在这个过程中,编译器会进行一系列的语法检查。如果编译通过,就会把对应的CPP转换成OBJ文件。
2、链接器:
当链接器进行链接的时候,首先决定各个目标文件在最终可执行文件里的位置。然后访问所有目标文件的地址重定义表,对其中记录的地址进行重定向(加上一个偏移量,即该编译单元在可执行文件上的起始地址)。
然后遍历所有目标文件的未解决符号表,并且在所有的导出符号表里查找匹配的符号,并在未解决符号表中所记录的位置上填写实现地址。最后把所有的目标文件的内容写在各自的位置上,再作一些另的工作,就生成一个可执行文件。
⑻ 怎么查 西门子PLC step7 中的符号表中的符号在程序的什么地方使用。
1、在西门子PLC step7中,直接按照选项→参考数据→显示的顺序进行点击。
⑼ 如何建立符号表
Symbol Tables
为了维持静态作用域的程序里各个名字的轨迹,编译器需要依靠一种称为符号表的数据结构。从最基本的层次上看,符号表就是一个字典:它把名字映射到编译器已知的有关信息。这里最基本的操作是把一个新映射关系(名字对象约束)放入表里,以及(非破坏性的)用一个给定名字去提取映射下的信息,以后我们把
这两个操作分别称为insert和lookup。大部分语言里的静态作用域规则还提出了另外的复杂性,它们要求在程序里的不同部分有不同的引用环境。为了处理作用域规则,我们可能希望简单增加一个remove操作。由于编译器在语义分析阶段要从头到尾扫描代码,这样它就可以在某个作用域开始时插入新约束,在作用域最后撤销它们。但是,存在一些因素使这种直接做法并不实际。
¨ 在许多有着嵌套作用域的语言里,内层声明的效果可以遮蔽外层声明,这就意味着符号表必须有能力为一个给定名字保存任意数目的映射。lookup操作必须返回最内层的映射,到作用域结束时还必须使外层映射重新变成可见的。
¨ 类Algol语言里的记录(结构)具有某种作用域性质,但却又不享有作用域那样的良好嵌套结构。当语义分析器看到一个记录声明时,它就必须记下各个记录域的名字(它们也是递归的,因为记录可以嵌套)。在这种声明结束时,各个域的名字又必须变成不可见的。然而,在此之后,一旦这一记录类型的某个变量出现在程序的正文里(例如在my_rec.field_name),在引用中位于圆点之后的部分,这些域名又必须立即重新变成可见的。在Pascal和另一些有with语句的语言里,记录域的名字还应该在多个语句的上下文里变成可见的。
¨ 某些时候一些名字有可能在它们被声明之前使用,即使在类Algol语言里情况也如此。举例说,Algol 60和Algol 68都允许标号的向前引用。Pascal避免了这种情况,它要求标号必须在作用域开始处声明,但还是允许指针声明的向前引用:
type
company = record
CEO : ^person; (* forward reference *)
...
end;
person = record
employer : ^company;
...
end;
¨ Pascal和其他语言都允许子程序的向前声明,以便支持相互递归:
procere Q (A, B : integer); forward;
procere P (A, B : integer);
begin
...
Q (3, 4);
...
end;
procere Q; (* parameters are not repeated in Pascal *)
begin
...
P (4, 5);
...
end;
在看到这段代码里的向前声明时,语义分析器必须记住Q的参数,以便后面可以在Q的体里使它们重新变成可见的,但在此期间又必须使它们成为不可见的。这种操作类似于记住记录域的名字。
¨ 虽然我们有可能希望在作用域结束时忘记有关的名字,甚至回收这些名字在符号表里占据的空间,但有关它们的信息仍需要保存起来,以便符号纠错系统(symbolic debugger)使用。这种纠错系统是非常有用的工具,用户可以借助它方便地操纵程序,如启动程序,停住它,读出或者修改程序里的数据等等。为了分析来自用户的高级名字(例如,要求打印出my_firm^.revenues[1999] 的值),符号纠错程序必须能访问编译器的符号表。为了使符号表在运行时也可以用,编译器通常会把这个表保存到最后的机器语言程序里的某个隐蔽的部分。
静态作用域的大部分变化都可以通过扩充基本符号表的方式处理,通过增加一对enter_scope和leave_scope操作维持可见性的轨迹。任何东西都不会从符号表里删除,在整个编译阶段所有的结构都保留着,最后还要为纠错系统使用而保存起来。带有可见性处理的符号表可以以多种不同方式实现,下面描述的方式归功于LeBlanc和Cook [CL83]。
在遇到每个作用域时赋给它一个序列号。给定最外层的作用域(其中包含着预定义的标识符)编号0,包含用户定义全局名字的作用域给以编号1。其他作用域按遇到它们的顺序进行编号。所有的编号互不相同,它们并不表示词法嵌套的层次,但也有一点,嵌套于内部的子程序的编号自然会大于它们的外围作用域的编号。
所有的名字都被放入一个大的散列表里,以名字作为关键码,无论其作用域如何。表里的每项都包含一个符号名,其类属(变量、常量、类型、过程、域名字、参数等等),作用域编号,类型(一个指向另一符号表项的指针),以及另一些特定类属所拥有的信息。
除了这一散列表之外,符号表还包含一个作用域堆栈,它按顺序指明组成当前引用环境的所有作用域。在语义分析器扫描程序的过程中,在进入或退出程序时分别压入或者弹出这个堆栈。作用域堆栈的项里包含着作用域编号,指明这一作用域是否为闭的,有些情况下还可以有另外一些信息。
图3.13 LeBlanc-Cook符号表的lookup算法。
当需要到表里查找名字时,我们会顺着某个适当的散列表链向下找,这样就会找到要找的名字所对应的一些项。对于每个匹配项,我们都向下扫描作用域堆栈,看看这个项所在的作用域是否可见。这种堆栈查看的深度不应超过最上面的闭作用域。要把导入项和导出项变为在它们的作用域之外可见的,方法就是在表里建立另外的项,让这些项里包含着指向实际项的指针。对于所有带有作用域编号0的项,我们都不需要去检查作用域堆栈,因为它们是渗透性的。图3.13里是lookup算法的伪代码。
图3.14的右下角是一个Mola-2程序的梗概,图中其余部分展现的是在过程P2里with语句处的引用环境的符号表配置情况。作用域堆栈里包含4个项,分别表示那个with语句,过程P2,模块M和全局作用域。with语句的作用域指明了在这一特定作用域里的(域)名字属于哪个记录变量。最外面的渗透性作用域没有显式表示。
图3.14 一个Mola-2例子程序的LeBlanc-Cook符号表。作用域堆栈表示在过程P2里with语句的引用环境。为清楚起见,许多指向符号表里对应于integer和real的项都用带括号的 (1) 和 (2) 表示,没有画出箭头。
因为这里的散列表以名字作为关键码,特定名字的所有项都出现在同一个散列链里。在这个例子里,散列冲突导致A2、F2和T出现在同一个链里。变量V和I(M的I)有另外的项,使它们跨过闭作用域M的边界后仍为可见的。当我们处于P2里时,对于I的查找操作将找到P2的I,M的I里的两个项都不可见。类型T的项指明了在with语句期间放入作用域堆栈的作用域编号。每个子程序的项里包含了一个头指针,指向子程序参数的链接表,以便做调用分析时使用(图中没有给出这些链的一些链接)。在代码生成过程中,许多符号表项还可能包含另外的域,表示例如对象大小和运行时地址等等信息。
图片信息,看参考资料。。。