讀古今文學網 > C語言解惑 > 14.7 變量的存儲地址分配 >

14.7 變量的存儲地址分配

【例14.21】典型變量存儲地址分配演示。


#include <stdio.h>
#include <stdlib.h>
int a
; static int b
; char ch
;          //
沒有初始化
int c=10
; char s1=\"OKwe\"
; char c1=\'w\'
;     //
初始化
const int i=25
;                    //
全局常量
char *p=\"We are here
!\"
;               //
全局常量
void ttt
()                         //
函數
{
  printf
(\"
結束n\"
);
}
int main
( 
)
{
      int num=0
; const int n=25
; volatile int result=15
;
      static int m=15
;
      int *p1
, *p2
;
      char *pc=\"She is here
!\"
;
      char st=\"We are here
!\"
;
      p2=
(int *
)malloc
(100
);          //
分配堆
      p1=&num
;
      printf
(\"
全局沒初始化:t%p %p %pn\"
,&a
,&b
,&ch
);
      printf
(\"
全局初始化:t%p %p %pn\"
,&c
,&c1
,&s1
);
      printf
(\"
全局常量:t%p %pn\"
,&i
,&p
);
      printf
(\"
局部常量:t%p %p %p %pn\"
,&n
,pc
,p2
,&m
);
      printf
(\"
局部變量:t%p %p %p %p %p %pn\"
,&num
,&result
,p1
,&pc
,&p1
,&p2
);
      printf
(\"
兩種常量:t%p %p %p %pn\"
,p
,&p
, pc
,&pc
);
      printf
(\"
局部字串:t%p %p %p %pn\"
,st
,&st
, pc
,&pc
);
      printf
(\"
函數地址:t%p %pn\"
,&main
,&ttt
);
      return 0
;
}
  

程序輸出結果如下:


全局沒初始化:      004237C8 004237CC 004237D0
全局初始化:        004232F8 00423301 004232FC
全局常量:          00420F2C 00423304
局部常量:          0012FF78 0042001C 00430070 00423308
局部變量:          0012FF7C 0012FF74 0012FF7C 0012FF68 0012FF70 0012FF6C
兩種常量:          00420F7C 00423304 0042001C 0012FF68
局部字串:          0012FF58 0012FF58 0042001C 0012FF68
函數地址:          0040100A 0040100F
  

可以把存儲區分為代碼區、文字常量區、全局區(靜態區)、堆和棧。代碼區用來存放程序的二進制代碼,由系統負責。文字常量區存放字符串常量。這一點要特別注意,不管是全局字符串常量,還是局部字符串常量,都由系統分配在文字常量區,這個區位於全局區,當然,局部字符串常量只是存儲在文字常量區,並不像全局字符串那樣可以共享。主程序的字符串常量pc就是這種情況,它是局部常量,但系統並不將它分配在棧區,雖然分配在全局區(存儲在0042001C),但又不能像全局字符串常量p(存儲在00423304)那樣提供共享。區別是指針常量p的&p被分配在全局區,指針pc的&pc被分配在棧區。程序中將它們單獨輸出出來(見第6行輸出)供對比。局部字串一行將st和pc進行比較,通過對這些常量和變量的分配方式,能提高編程的效率,這將在後面專門敘述。

注意:本程序的文字常量和符號常量(const)是相隔較遠的(見第3行輸出)。其實,const申明的變量有時候根本不存在,全部在編譯的時候被替換成具體的值,即使聲明的變量存在,也不在文字常量區。文字常量區僅用於保存字符串常量。

全局區(靜態區)是存放全局變量和靜態變量的存放區域,分為已初始化和未初始化兩個區域,已初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束後由系統釋放。對比第1行和第2行的結果即知。注意第3行i和p的分配地址以及p和c的分配地址,就可以進一步理解文字常量的含義。其實,我們又把編譯器處理全局變量所存儲的區域稱作數據區,「=」號並不作為賦值語句,僅作為初始值。函數的地址也是放在數據區,與全局變量存放的區域接近。

堆區(heap)一般由程序員分配和釋放。如果程序員沒有釋放,則在程序結束後,由操作系統收回。程序為p2分配的地址不在棧區,是堆區。

棧區由編譯器自動分配和釋放,存放函數的參數、局部變量等。在函數里面,除了文字常量之外,其他的局部常量與局部變量都一樣,均由系統分配在棧區,程序結束後由系統釋放。這些都是函數調用的基礎,後面將常常涉及這些知識。

也把局部變量分配的區域稱為程序區,將「=」號解釋為一條賦值指令,所以分配在棧上。將它聲明為局部變量,並跟聲明為全局變量相比,則增加了指令數量,但減少了數據量。

必須注意,系統對不同數據類型採取的存儲方法不一樣。因為聯合的元素使用同一地址,所以可以使用聯合演示一下對同一地址的內容採取不同類型輸出的結果。

【例14.22】演示同一數據使用不同數據類型的結果。


#include <stdio.h>
union uda {
     int num
;
     unsigned char str[4]
;
     float f
;
}u
;
void main
( 
)
{
     int i=0
;
     u.f = 1.0
;
     printf
(\"f = %fn\"
, u.f
);
     for
(i=0
; i<4
; i++
)
           printf
(\"str[%d] = %#xn\"
, i
, u.str[i]
);
     printf
(\"num= %#xn\"
, u.num
);
     u.f = 0.0
;
     printf
(\"f = %fn\"
, u.f
);
     for
(i=0
; i<4
; i++
)
           printf
(\"str[%d] = %#xn\"
, i
, u.str[i]
);
     u.num = 0x3f800000
;
     printf
(\"num= %#xn\"
, u.num
);
     printf
(\"f = %fn\"
, u.f
);
}
 

程序中先設置u.f=1,通過輸出u.str的內容


str[0]=0
str[1]=0
str[2]=0x80
str[3]=0x3f
  

可以知道它在內存的存儲方式為0x3f 0x80 0x 00 0x00。這時的u.num也是這個地址,因此它的值應該為0x3f800000。

可以通過先設置u.num,再打印u.f來驗證這一點。為了更有說服力,先通過u.f=0.0將這個內存的內容置為0,然後使用


u.num = 0x3f800000
;
  

語句設置這段內存,這時u.f的內容應該為1.0,strd的內容為0 0 0x80 0x3f。對照下面的輸出結果,驗證這個結論。


f = 1.000000
str[0] = 0
str[1] = 0
str[2] = 0x80
str[3] = 0x3f
num= 0x3f800000
f = 0.000000
str[0] = 0
str[1] = 0
str[2] = 0
str[3] = 0
num= 0x3f800000
f = 1.000000
str[0] = 0
str[1] = 0
str[2] = 0x80
str[3] = 0x3f
  

由此可知,使用中一定不能混淆數據類型。