當前位置:首頁 » 編程軟體 » 編譯原理參數傳遞的4種方式

編譯原理參數傳遞的4種方式

發布時間: 2023-05-13 05:31:00

『壹』 參數傳遞的方式

樓主說的是C++么?
參數傳遞有三種:
傳值(value),傳址(address),和傳引用(reference)

傳值時子函數(被調用者)復制父函數(調用者)傳遞的值,這樣子函數無法改變父函數變數的值

傳址時父函數將變數的地址傳遞給子函數,這樣子函數可以能過改寫地址里的內容改變父函數中的變數

傳引用則是一種看起來像傳值調用,而實際上功能同傳址一樣的傳遞方式。子函數可以改寫父函數的變數值。

『貳』 c語言函數參數傳遞的是值還是拷貝

一 參數
1 所有的參數傳遞,都是傳遞值的拷貝。(如果想知道為什麼,去學習編譯原理的函數調用的參數壓棧和出棧對應內容)。
2 C傳指針進去,其實也是把這個指針值按拷貝傳送進去。但是因為指針值指向一塊外部內存空間(其實更多是堆空間,或外層棧變數空間),所以感覺可以在函數里改變外部變數。其實本質還是按拷貝傳遞,只是傳遞進去的是一個訪問變數的渠道。
因此,如果我們希望函數內能改變外部的指針值,往往傳進去的是指針變數的指針。呵呵,很多初學C的程序員,對**非常難理解。

二 返回值
返回值是按拷貝傳遞,函數出棧後,會傳出一個值,該值在調用函數的代碼段的生命周期里一直有效。相當與調用點形成一個匿名的棧變數。

變數a = function(); 而a並不等於函數里return的那個值。
其實function()執行結果自身就是一個匿名變數。(其實編譯器會檢查語法,如上面a=function這樣的語法,匿名變數不會生成,直接使用a變數拷貝返回值)
例如: function()返回int值。 完全可以 int x = function() + 6;//注意:+運算時,函數已經執行完畢,所有函數出棧操作已經結束。
很明顯function()必須有一個變數或常量參與計算,而函數里return的值會隨函數調用結束出棧而被刪除,所以必須拷貝構造傳遞出來。

『叄』 編譯原理 四元式

四元式是一種比較普遍採用的中間代碼形式。

代碼段的四元式表達式:

101 T:=0 (表達式為假的出口)

103 T:=1 (表達式為真的出口)

因為用戶的表達式只有一個A<B,因此A<B的真假出口就是表達式的真假出口,所以

100: if a<b goto 103 (a<b為真,跳到真出口103)

101: T:=0(否則,進入假出口)

102: goto 104 (要跳過真出口,否則T的值不就又進入真出口了,為真)

103: T:=1

104:(程序繼續執行)

(3)編譯原理參數傳遞的4種方式擴展閱讀:

四元式是一種更接近目標代碼的中間代碼形式。由於這種形式的中間代碼便於優化處理,因此,在目前許多編譯程序中得到了廣泛的應用。

四元式實際上是一種「三地址語句」的等價表示。它的一般形式為:

(op,arg1,arg2,result)

其中, op為一個二元 (也可是一元或零元)運算符;arg1,arg2分別為它的兩個運算 (或操作)對象,它們可以是變數、常數或系統定義的臨時變數名;運算的結果將放入result中。四元式還可寫為類似於PASCAL語言賦值語句的形式:

result ∶= arg1 op arg2

需要指出的是,每個四元式只能有一個運算符,所以,一個復雜的表達式須由多個四元式構成的序列來表示。例如,表達式A+B*C可寫為序列

T1∶=B*C

T2∶=A+T1

其中,T1,T2是編譯系統所產生的臨時變數名。當op為一元、零元運算符 (如無條件轉移)時,arg2甚至arg1應預設,即result∶=op arg1或 op result ;對應的一般形式為:

(op,arg1,,result)

(op,,,result)

『肆』 四種文法的類型(編譯原理)

喬姆斯基(Chomsky)按產生式的類型把文法分為四種類型:0、1、2、3型文法。

*在下文中的產生式中,箭頭左邊的大寫字母為嚴格的非終結符,而其左邊的小寫字母不嚴格要求為非終結符,如[0型文法]中的第2條產生式。

【0型文法】

產生式形式:α→β

要求:箭頭左邊的α 至少 含有 一個非終結符 , 其餘 不加任何限制

    例如,G:C→AaB

                     aA→a

                     B→b|Bb

【1型文法】

產生式形式:α→β

要求: |α|≤|β| (產生式左端的長度<=右端的長度),S→ε除外。

例如G: C→aAB

               aA→aBa

               B→b|Bb 

【2型文法】(上下文無關文法)

產生式形式:A→β,A∈VN(終結符) ,β∈V *(VN∪VT,即可為終結符也可為非終結符) 

說明:當以β替換A時,與A的上下文環境無關;

          大部分程序設計語言近似於2型文法。

【3型文法】(正規文法 / 右線性文法)

產生式形式:A→a,A→aB,

說明:a∈VT(終結符) ,  A,B∈VN(非終結符),即產生式右端的第一個符號必須為 終結符

例如 G:A→aB

              B→b|bB

【其他說明】對於這四種類型的文法:

*包含關系:0 > 1 > 2 > 3 (以'>'代替包含符,'A>B'譯為A包含B)

*嚴格程度:3 > 2 > 1 > 0

*判斷文法所屬類型的順序:3 → 2 → 1 → 0

『伍』 編譯原理四——代碼優化

1、基本塊的劃分方法:

3、DAG圖實現基本塊的優化

1、程序流圖與循環
控制流程圖就是有唯一首節點的有向圖,用三元組G=(N,E,n 0 )表示(節點集,邊集,首節點)節點集就是基本塊集,有向邊表示如下:基本塊i出口語句不是轉向語句或停語句,i與緊隨其後的基本塊j有有向邊。或者i出口轉向j入口語句。
2、循環:程序流圖里的一個節點序列強連通,任意兩個節點都有至少一條通路,它們中有且只有一個入口節點。(從序列外某節點有一條有向邊引導它,或他是程序流圖的首節點。
3、找循環:
必經節點集:從流圖首節點出發,到n的任意通路都要經過m,m是n的必經節點,記為mDOMn;流圖中結點n的所有必經節點的集合稱為節點n的必經結點集,極為D(n)。
DOM的性質:自反性:流圖中任意節點a,都有aDOMa。傳遞性:aDOMb,bDOMc則aDOMc。反對稱性:aDOMb,bDOMa,a=b。DOM是一個偏序關系,任何節點n的必經節點集是一個有序集。
必經節點的求法:一定包括自己好吧。。。。。。必經節點集就是前驅節點必經節點集的交集加自己沒准。
找回邊:假設a b是流圖中的一條有向邊,如果bDOMa,則a b是流圖中的一條回邊。已知有向邊n d是一條回邊,則由它組成的循環就是由結點d、結點n以及有通路到達n但該通路不經過d的所有結點組成的。
4、可規約流圖:當且僅當一個流圖除去回邊後,其餘邊構成一個無環路流圖。性質:1. 圖中任何直觀環路都是循環。2. 找到所有回邊可以對應找出所有循環。3. 循環或嵌套或不相交(可能有公共入口節點),goto語句不可跳入循環。

5、循環優化

『陸』 如何區分傳值與傳址

區別:對形參的影響不同

1、在譽改傳值中函數參數壓棧的是參數的副本,任何的修改是在副本上作用,沒有作用在原來的變數上。

2、傳址中壓棧的是指針變數的副本,當你對指針解指針操作時,其值是指向原來的那個變數,所以對原來變數操作。

(6)編譯原理參數傳遞的4種方式擴展閱讀

函數傳參有三種傳參方式:傳值、傳址、傳引用。

1、按值傳遞

(1)形參和實參各佔一個獨立的存儲空間。

(2)形參的存儲空間是函數被調用時才分配的,調用開始戚虛殲,系統為形參開辟高沖一個臨時的存儲區,然後將各實參傳遞給形參,這是形參就得到了實參的值。

2、地址傳遞

地址傳遞與值傳遞的不同在於,它把實參的存儲地址傳送給形參,使得形參指針和實參指針指向同一塊地址。因此,被調用函數中對形參指針所指向的地址中內容的任何改變都會影響到實參。

3、引用傳遞

引用傳遞是以引用為參數,則既可以使得對形參的任何操作都能改變相應數據,又使函數調用方便。引用傳遞是在形參調用前加入引用運算符「&」。

引用為實參的別名,和實參是同一個變數,則他們的值也相同,該引用改變則它的實參也改變。





『柒』 c語言 為什麼主函數調用函數average的實參是數組名score,而不是整個數組

如果一個函數以一維數組為參數,我們可以這樣聲明這個函數
void func(int* a) ;void func(int a[]) ;void func(int a[3]) ;

實際上,這三種形式是等價的,在使用數組做參數時,編譯器會自動將數組名轉換為指向數組第一個元素的指針,為什麼呢?這要從參數的傳遞方式說起,參數有三種傳遞方式,按值傳遞,按指針傳遞,按引用傳遞,分別如下
void Test(int a) ;void Test(int* a) ;void Test(int& a) ;

第一種方式傳遞的是a的一個副本
第二種方式傳遞的是指向a的指針的一個副本
第三種方式傳遞的是指向a的引用的一個副本
既然都是副本,那麼就存在拷貝到過程,但是,數組是不能直接拷貝的,也就是不能像下面這樣
int a[3] = {1, 2, 3} ;int b[](a) ; // errorint b[3] ;b = a ; // error
不能用一個數組初始化另一個數組,也不能將一個數組直接賦值給另外一個數組,如果想復制數組,唯一的辦法就是逐個元素復制。int a[3] = {1, 2, 3} ;int b[3] ;for (int i = 0; i < 3; ++i){ b[i] = a[i] ;}

既然數組不能拷貝,那麼參數該如何傳遞呢?於是編譯器就將數組名轉換成了指向第一個元素的指針,指凱跡和針是可以拷貝的。但是這也引發了另外一個問題。我們無法只通過數組名得知數組元素的個數。看下面的代碼
void Test(int a[3]){ for (int i = 0; i < 5; ++i) { cout << a[i] << endl ; }}

明明只傳遞了三個元素的數組,為什麼輸出5個元素?前面已經說了,數組被轉換成了指向第一個元素的指針,所以上面的代碼和下面的相同
void Test(int* a) //我只知道a是個指針,跟本不知道a指向多少個元素{ for (int i = 0; i < 5; ++i) { cout << a[i] << endl ; }}

編譯器根本不知奧數組a有多少個元素,它甚至不知道a是數組!如何解決呢,一種辦法是再加一個參數,指定元素個數
void Test(int* a, int n){ for (int i = 0; i < n; ++i) { cout << a[i] << endl ; }}

另外一種辦法是州祥傳遞數組的引用,這才是本文的重點,唉,前面這么多廢話:(
void Test(int (&a)[3]){ for (int i = 0; i < 3; ++i) { cout << a[i] << endl ; }}

這樣寫數組a就不會被轉換為指針了,而且有了元素個數的信息,調用的時候,也必須傳遞一個含有3個元素的數組
int a[3] = {1, 2, 3} ;Test(a) ; // okint b[1] = {1} ;Test(b) ; // error, can not convert parameter a from int[1] to int(&)[3]

1、對於一維數組來說,數組作為函數參數傳遞,實際上傳遞了一個指向數組的指針,在c編譯器中,當數組名作為函盯盯數參數時,在函數體內數組名自動退化為指針。此時調用函數時,相當於傳址,而不是傳值,會改變數組元素的值。
例如:void fun(int a[]); 若在fun函數中有a[i]++;等語句,那麼對應的數組元素會被修改,調用時直接用fun(a);即可。
2、對於高維數組來說,可以用二維數組名作為實參或者形參,在被調用函數中對形參數組定義時可以指定所有維數的大小,也可以省略第一維的大小說明,如:
void fun(int array[3][10]);
void fun(int array[][10]);
二者都是合法而且等價,但是不能把第二維或者更高維的大小省略,如下面的定義是不合法的:
void fun(int array[][]);
因為從實參傳遞來的是數組的起始地址,在內存中按數組排列規則存放(按行存放),而並不區分行和列,如果在形參中不說明列數,則系統無法決定應為多少行多少列,不能只指定一維而不指定第二維,下面寫法是錯誤的:
void fun(int array[3][]);
實參數組維數可以大於形參數組,例如形參數組定義為:
void fun(int array[3][10]);
而實參數組定義為:
int array[5][10];
這時形參數組只取實參數組的一部分,其餘部分不起作用。
可以看到,將二維數組當作參數的時候,必須指明所有維數大小或者省略第一維的,但是不能省略第二維或者更高維的大小,這是由編譯器原理限制的。學編譯原理的時候應該 知道編譯器是這樣處理數組的:
對於數組 int p[m][n];
如果要取p[i][j]的值(i>=0 && i<m && 0<=j && j < n),編譯器是這樣定址的,它的地址為:
p + i*n + j;
從以上可以看出,如果我們省略了第二維或者更高維的大小,編譯器將不知道如何正確的定址,即這里的n值是在形參定義的時候就要明確知道的。但是我們在編寫程序的時候卻需要用到各個維數都不固定的二維數組 作為參數,這就難辦了,編譯器不能識別阿,怎麼辦呢?不要著急,編譯器雖然不能識別,但是我們完全可以不把它當作一個二維數組,而是把它當作一個普通的指針,再另外加上兩個參數指明各個維數,然後我們為二維數組手工定址,這樣就達到了將二維數組作為函數的參數傳遞的目的,根據這個思想,我們可以把維數固定 的參數變為維數隨即的參數,例如:
void fun(int array[3][10]);
void fun(int array[][10]);
變為:
void fun(int **array, int m, int n);
在轉變後的函數中,array[i][j]這樣的式子是不對的(不信,大家可以試一下),因為編譯器不能正確的為它定址,所以我們需要模仿編譯器的行為把array[i][j]這樣的式子手工轉變為:
*(*array + n*i + j);
在調用這樣的函數的時候,需要注意一下,如下面的例子:
int a[3][3] = { {1, 1, 1}, {2, 2, 2}, {3, 3, 3}};
fun(a, 3, 3);
根據不同編譯器不同的設置,可能出現warning 或者error,可以進行強制轉換如下調用:
fun((int**)a, 3, 3);

『捌』 C語言編譯原理是什麼

編譯共分為四個階段:預處理階段、編譯階段、匯編階段、鏈接階段。

1、預處理階段:

主要工作是將頭文件插入到所寫的代碼中,生成擴展名為「.i」的文件替換原來的擴展名為「.c」的文件,但是原來的文件仍然保留,只是執行過程中的實際文件發生了改變。(這里所說的替換並不是指原來的文件被刪除)

2、匯編階段:

插入匯編語言程序,將代碼翻譯成匯編語言。編譯器首先要檢查代碼的規范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤後,編譯器把代碼翻譯成匯編語言,同時將擴展名為「.i」的文件翻譯成擴展名為「.s」的文件。

3、編譯階段:

將匯編語言翻譯成機器語言指令,並將指令打包封存成可重定位目標程序的格式,將擴展名為「.s」的文件翻譯成擴展名為「.o」的二進制文件。

4、鏈接階段:

在示例代碼中,改代碼文件調用了標准庫中printf函數。而printf函數的實際存儲位置是一個單獨編譯的目標文件(編譯的結果也是擴展名為「.o」的文件),所以此時主函數調用的時候,需要將該文件(即printf函數所在的編譯文件)與hello world文件整合到一起,此時鏈接器就可以大顯神通了,將兩個文件合並後生成一個可執行目標文件。

『玖』 編譯原理傳地址問題

傳址的話,這么講你應該更明白,因為y(A):=x+y(1),y(1)=x+y(2),這時的y(2)才等於A,所以y:=x+x+y

熱點內容
php入門手冊 發布:2025-07-10 14:42:24 瀏覽:791
手機如何設密碼鎖屏 發布:2025-07-10 14:17:06 瀏覽:803
java求絕對值 發布:2025-07-10 14:10:55 瀏覽:653
usb調試開關在哪裡安卓 發布:2025-07-10 13:59:55 瀏覽:78
資料庫維度 發布:2025-07-10 13:54:31 瀏覽:799
c語言位域的賦值 發布:2025-07-10 13:54:30 瀏覽:583
查成績密碼忘了怎麼辦 發布:2025-07-10 13:52:21 瀏覽:819
java死 發布:2025-07-10 13:51:30 瀏覽:996
車輛設施配置有哪些 發布:2025-07-10 13:42:28 瀏覽:820
java的成員 發布:2025-07-10 13:38:59 瀏覽:720