在學習c語言提高總結了筆記,并分享出來。有問題請及時聯系部落客:Alliswell_WP,轉載請注明出處。
02-c提高02day
1、函數調用模型

棧(stack)是現代計算機程式裡最為重要的概念之一,幾乎每一個程式都使用了棧,沒有棧就沒有函數,沒有局部變量,也就沒有我們如今能見到的所有計算機的語言。在解釋為什麼棧如此重要之前,我們先了解一下傳統的棧的定義:
在經典的計算機科學中,棧被定義為一個特殊的容器,使用者可以将資料壓入棧中(入棧,push),也可以将壓入棧中的資料彈出(出棧,pop),但是棧容器必須遵循一條規則:先入棧的資料最後出棧(First In First Out,FIFO).
在經典的作業系統中,棧總是向下增長的。壓棧的操作使得棧頂的位址減小,彈出操作使得棧頂位址增大。
棧在程式運作中具有極其重要的地位。最重要的,棧儲存一個函數調用所需要維護的資訊,這通常被稱為堆棧幀(Stack Frame)或者活動記錄(Activate Record).一個函數調用過程所需要的資訊一般包括以下幾個方面:
■函數的傳回位址;
■函數的參數;
■臨時變量;
■儲存的上下文:包括在函數調用前後需要保持不變的寄存器。
1 int func(int a,int b){
2
3 int t_a=a;
4 int t_b=b;
5
6 return t_a+t_b;
7 }
8
9 int main(){
10
11 int ret=func(10,20);
12 int a=20;
13
14 return EXIT_SUCCESS;
15 }
2、調用慣例
現在,我們大緻了解了函數調用的過程,這期間有一個現象,那就是函數的調用者和被調用者對函數調用有着一緻的了解,例如,它們雙方都一緻的認為函數的參數是按照某個固定的方式壓入棧中。如果不這樣的話,函數将無法正确運作。
如果函數調用方在傳遞參數的時候先壓入a參數,再壓入b參數,而被調用函數則認為先壓入的是b,後壓入的是a,那麼被調用函數在使用a,b值時候,就會颠倒。
是以,函數的調用方和被調用方對于函數是如何調用的必須有一個明确的約定,隻有雙方都遵循同樣的約定,函數才能夠被正确的調用,這樣的約定被稱為”調用慣例(Calling Convention)"一個調用慣例一般包含以下幾個方面:
函數參數的傳遞順序和方式
函數的傳遞有很多種方式,最常見的是通過棧傳遞。函數的調用方将參數壓入棧中,函數自己再從棧中将參數取出。對于有多個參數的函數,調用慣例要規定函數調用方将參數壓棧的順序:從左向右,還是從右向左。有些調用慣例還允許使用寄存器傳遞參數,以提高性能。
棧的維護方式
在函數将參數壓入棧中之後函數體會被調用此後需要将被壓入棧中的參數全部彈出,以使得棧在函數調用前後保持一緻。這個彈出的工作可以由函數的調用方來完成,也可以由函數本身來完成。
為了在連結的時候對調用慣例進行區分,調用慣例要對函數本身的名字進行修飾。不同的調用慣例有不同的名字修飾政策。
事實上,在c語言裡,存在着多個調用慣例,而預設的是cdecl.任何一個沒有顯示指定調用慣例的函數都是預設是cdecl慣例。比如我們上面對于func函數的聲明,它的完整寫法應該是:
int_cdecl func(int a,int b);
注意:cdecl不是标準的關鍵字,在不同的編譯器裡可能有不同的寫法,例如gcc裡就不存在cdecl 這樣的關鍵字,而是使用_attribute((cdecl)).
3、函數變量傳遞分析
4、棧的生長方向和記憶體存放方向
1 //1.棧的生長方向
2
3 void test01(){
4
5 int a=10;
6 int b=20;
7 int c=30;
8 int d=40;
9
10 printf("a=sd\n",&a);
11 printf("b=sd\n",&b);
12 printf("c=sd\n",&c);
13 printf("d=sd\n",&d);
14 //a的位址大于b的位址,故而生長方向向下
15 }
16
17 //2.記憶體生長方向(小端模式)
18 void test02(){
19
20 //高位位元組->地位位元組
21 int num=0xaabbccdd;
22 unsigned char* p=#
23
24 //從首位址開始的第一個位元組
25 printf("%x\n",*p);
26 printf("%x\n",*(p+1));
27 printf("%x\n",*(p+2));
28 printf("%x\n",*(p+3));
29 }
5、指針