編譯語言符號表
㈠ 什麼是符號表 符號表有哪些重要作用
符號表是一種用於語言翻譯器(例如編譯器和解釋器)中的數據結構。在符號表中,程序源代碼中的每個標識符都和它的聲明或使用信息綁定在一起,比如其數據類型、作用域以及內存地址。
符號表的作用:符號表在編譯程序工作的過程中需要不斷收集、記錄和使用源程序中一些語法符號的類型和特徵等相關信息。這些信息一般以表格形式存儲於系統中。
如常數表、變數名表、數組名表、過程名表、標號表等等,統稱為符號表。對於符號表組織、構造和管理方法的好壞會直接影響編譯系統的運行效率。
(1)編譯語言符號表擴展閱讀
編譯程序按名字的不同種屬分別使用許多符號表,如常熟表。變數名表過程名表函數入口名表。符號表的關鍵字域(段)就是符號名稱等長關鍵字域(段)符號表。
不等長關鍵欄位符號表,採用關鍵字詞的索引結構。關鍵字在符號表的查找中相當重要hash函數構造方法就是以取關鍵字的值不同區分,如直接定址伐數字分析法折疊法。
㈡ c語言基本符號
c語言基本符號
C++既可用於面向過程的結構化程序設計,又可用於面向對象的程序設計,是一種功能強大的混合型的程序設計語言。下面是我收集的關於c語言基本符號,希望大家認真閱讀!
運算符的種類C語言的運算符可分為以下幾類:
1.算術運算符
用於各類數值運算。包括加(+)、減(-)、乘(*)、除(/)、求余(或稱模運算,%)、自增(++)、自減(–)共七種。
2.關系運算符
用於比較運算。包括大於(>)、小於(<)、等於(==)、>=)、小於等於(<=)和不等於(!=)六種。
3.邏輯運算符
用於邏輯運算。包括與(&&)、或(||)、非(!)三種。
4.位操作運算符
參與運算的量,按二進制位進行運算。包括位與(&)、位或(|)、位非(~)、位異或(^)、左移(<<)、右移(>>)六種。
5.賦值運算符
用於賦值運算,分為簡單賦值(=)、復合算術賦值(+=,-=,*=,/=,%=)和復合位運算賦值(&=,|=,^=,>>=,<<=)三類共十一種。
6.條件運算符
這是一個三目運算符,用於條件求值(?:)。
7.逗號運算符
用於把若干表達式組合成一個表達式(,)。
8.指針運算符
用於取內容(*)和取地址(&)二種運算。
9.求位元組數運算符
用於計算數據類型所佔的位元組數(sizeof)。
10.特殊運算符
有括弧(),下標[],成員(→,.)等幾種。
1.C的數據類型
基本類型,構造類型,指針類型,空類型
2.基本類型的分類及特點
類型說明符 位元組 數值范圍
字元型char 1 C字元集
基本整型int 2 -32768~32767
短整型short int 2 -32768~32767
長整型 long int 4-214783648~214783647
無符號型 unsigned 20~65535
無符號長整型 unsigned long 4 0~4294967295
單精度實型 float 43/4E-38~3/4E+38
雙精度實型 double81/7E-308~1/7E+308
3.常量後綴
L或l 長整型
U或u 無符號數
F或f 浮點數
4.常量類型
整數,長整數,無符號數,浮點數,字元,字元串,符號常數,轉義字元。
5.數據類型轉換
·自動轉換
在不同類型數據的混合運算中,由系統自動實現轉換,由少位元組類型向多位元組類型轉換。不同類型的量相互賦值時也由系統自動進行轉換,把賦值號右邊的類型轉換為左邊的類型。
·強制轉換
由強制轉換運算符完成轉換。
6.運算符優先順序和結合性
一般而言,單目運算符優先順序較高,賦值運算符優先順序低。算術運算符優先順序較高,關系和邏輯運算符優先順序較低。多數運算符具有左結合性,單目運算符、三目運算符、賦值。
7.表達式
表達式是由運算符連接常量、變數、函數所組成的式子。每個表達式都有一個值和類型。表達式求值按運算符的優先順序和結合性所規定的順序進行。
表示輸出類型的格式字元 格式字元意義
d 以十進制形式輸出帶符號整數(正數不輸出符號)
o 以八進制形式輸出無符號整數(不輸出前綴O)
x 以十六進制形式輸出無符號整數(不輸出前綴OX)
u 以十進制形式輸出無符號整數
f 以小數形式輸出單、雙精度實數
e 以指數形式輸出單、雙精度實數
g 以%f%e中較短的輸出寬度輸出單、雙精度實數
c 輸出單個字元
s 輸出字元串
標志字元為-、+、#、空格四種,其意義下表所示:
標志格式字元 標 志 意 義
- 結果左對齊,右邊填空格
+ 輸出符號(正號或負號)空格輸出值為正時冠以空格,為負時冠以負號
# 對c,s,d,u類無影響;對o類,在輸出時加前
綴o 對x類,在輸出時加前綴0x;對e,g,f 類當結果有小數時才給出小數點
格式字元串
格式字元串的一般形式為: %[*][輸入數據寬度][長度]類型,其中有方括弧[]的項為任選項。各項的意義如下:
1.類型
表示輸入數據的類型,其格式符和意義下表所示。
格式 字元意義
d 輸入十進制整數
o 輸入八進制整數
x 輸入十六進制整數
u 輸入無符號十進制整數
f或e 輸入實型數(用小數形式或指數形式)
c 輸入單個字元
s 輸入字元串
轉義字元
轉義字元是一種特殊的字元常量。轉義字元以反斜線」/」開頭,後跟一個或幾個字元。轉義字元具有特定的含義,不同於字元原有的意義,故稱「轉義」字元。例如,在前面各例題printf函數的格式串中用到的「/n」就是一個轉義字元,其意義是「回車換行」。轉義字元主要用來表示那些用一般字元不便於表示的控制代碼。
常用的轉義字元及其含義
轉義字元 轉義字元的意義
/n 回車換行
/t 橫向跳到下一製表位置
/v 豎向跳格
/b 退格
/r 回車
/f 走紙換頁
// 反斜線符」/」
/』 單引號符
/a 鳴鈴
/ddd 1~3位八進制數所代表的字元
/xhh 1~2位十六進制數所代表的字元
廣義地講,C語言字元集中的任何一個字元均可用轉義字元來表示。表2.2中的`/ddd和/xhh正是為此而提出的。ddd和hh分別為八進制和十六進制的ASCII代碼。如/101表示字?quot;A」,/102表示字母」B」,/134表示反斜線,/XOA表示換行等。轉義字元的使用
在C語言中,對變數的存儲類型說明有以下四種:
auto 自動變數
register 寄存器變數
extern 外部變數
static 靜態變數
自動變數和寄存器變數屬於動態存儲方式,外部變數和靜態變數屬於靜態存儲方式。在介紹了變數的存儲類型之後,可以知道對一個變數的說明不僅應說明其數據類型,還應說明其存儲類型。因此變數說明的完整形式應為:存儲類型說明符數據類型說明符 變數名,變數名…;例如:
static int a,b; 說明a,b為靜態類型變數
auto char c1,c2; 說明c1,c2為自動字元變數
static int a[5]={1,2,3,4,5}; 說明a為靜整型數組
extern int x,y; 說明x,y為外部整型變數
與指針有關的各種說明和意義見下表。
int *p; p為指向整型量的指針變數
int *p[n]; p為指針數組,由n個指向整型量的指針元素組成。
int (*p)[n]; p為指向整型二維數組的指針變數,二維數組的列數為n
int *p() p為返回指針值的函數,該指針指向整型量
int (*p)() p為指向函數的指針,該函數返回整型量
int **p p為一個指向另一指針的指針變數,該指針指向一個整型量。
指針變數的賦值
p可以有以下兩種方式:
(1)指針變數初始化的方法 int a;
int *p=&a;
(2)賦值語句的方法 int a;
int *p;
p=&a;
(1)取地址運算符&
(2)取內容運算符*
;㈢ c語言編程用的符號有哪些
如果真正掌握了C 語言,你就能很輕易的回答上來。這個問題就請讀者試著回答一下吧。本章不會像關鍵字一樣一個一個深入討論,只是將容易出錯的地方討論一下。
表(2.1)標准C 語言的基本符號
C 語言的基本符號就有20 多個,每個符號可能同時具有多重含義,而且這些符號之間相互組合又使得C 語言中的符號變得更加復雜起來。
你也許聽說過「國際C 語言亂碼大賽(IOCCC)」,能獲獎的人毫無疑問是世界頂級C程序員。這是他們利用C 語言的特點極限挖掘的結果。下面這個例子就是網上廣為流傳的一個經典作品:
#i nclude <stdio.h>
main(t,_,a)char *a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_,
main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?main(2,_+1,"%s %d %d
"):9:16:t<0?t<-72?main(_,t,"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw'
iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/"):t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1):0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:
uwloca-O;m.vpbks,fxntdCeghiry"),a+1);}
還沒發狂?看來你抵抗力夠強的。這是IOCCC 1988 年獲獎作品,作者是Ian Phillipps。
毫無疑問,Ian Phillipps 是世界上最頂級的C 語言程序員之一。你可以數數這裡面用了多少個符號。當然這里我並不會討論這段代碼,也並不是鼓勵你也去寫這樣的代碼(關於這段代碼的分析,你可以上網查詢)。
㈣ 如何查看ndk編譯的動態庫符號表
$ /path/to/ndk/buid/prebuilt/windows/arm-eabi-4.4.0/bin/arm-eabi-nm libs/armeabi/libsanangeles.so
00003600 T java_com_example_SanAngeles_DemoGLSurfaceView_nativePause
00003638 T Java_com_example_SanAngeles_DemoRenderer_nativeDone
0000367c T Java_com_example_SanAngeles_DemoRenderer_nativeInit
000035b4 T Java_com_example_SanAngeles_DemoRenderer_nativeRender
00003644 T Java_com_example_SanAngeles_DemoRenderer_nativeResize
00007334 a _DYNAMIC
0000740c a _GLOBAL_OFFSET_TABLE_
復制代碼
這里可以看到幾乎所有的函數名全局變數名都會被導出。其中有Java_com_example_SanAngeles_為前綴的JNI介面函數,有importGLInit這些普通函數,有freeGLObject這些局部(static)函數,還有sStartTick等全局變數名。其實在這個動態發布的時候,只需要導出java_com_開頭的jni函數就可以了,裡面這些細節函數名完全不需要暴露出來。
如何做到這一點呢?首先,我們需要了解gcc新引進的選項-fvisibility=hidden,這個編譯選項可以把所有的符號名(包括函數名和全局變數名)都強制標記成隱藏屬性。我們可以在Android.mk中可以通過修改LOCAL_CFLAGS選項加入-fvisibility=hidden來做到這一點,這樣編譯之後的.so看到的符號表為:
000033d0 t Java_com_example_SanAngeles_DemoGLSurfaceView_nativePause
00003408 t Java_com_example_SanAngeles_DemoRenderer_nativeDone
0000344c t Java_com_example_SanAngeles_DemoRenderer_nativeInit
00003384 t Java_com_example_SanAngeles_DemoRenderer_nativeRender
00003414 t Java_com_example_SanAngeles_DemoRenderer_nativeResize
00007104 a _DYNAMIC
㈤ c++編譯中的符號表是什麼東西
符號表是庫中所有函數,變數的總稱,用於連接過程.
㈥ 編譯原理對符號表進行操作有哪些
//----------------------------符號表---------------------------------------
//預定義
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......
}
㈦ 如何建立符號表
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語句期間放入作用域堆棧的作用域編號。每個子程序的項里包含了一個頭指針,指向子程序參數的鏈接表,以便做調用分析時使用(圖中沒有給出這些鏈的一些鏈接)。在代碼生成過程中,許多符號表項還可能包含另外的域,表示例如對象大小和運行時地址等等信息。
圖片信息,看參考資料。。。