天天看點

c語言程式的存儲區域與const關鍵字的使用方法

1.由c語言代碼(文本檔案)形成可執行程式(二進制檔案),需要經過編譯-彙編-連接配接三個階段。編譯過程把c語言文本檔案生成彙程式設計式,彙編過程把彙程式設計式形成二進制機器代碼,連接配接過程則将各個源檔案生成的二進制機器代碼檔案組合成一個檔案。

2.c語言編寫的程式經過編譯-連接配接後,将形成一個統一檔案,它由幾個部分組成。在程式運作時又會産生其他幾個部分,各個部分代表了不同的存儲區域:

1>代碼段(code或text)

代碼段由程式中執行的機器代碼組成。在c語言中,程式語句執行編譯後,形成機器代碼。在執行程式的過程中,cpu的程式計數器指向代碼段的每一條機器代碼,并由處理器依次運作。

2>隻讀資料段(ro data)

隻讀資料段是程式使用的一些不會被更改的資料,使用這些資料的方式類似查表式的操作,由于這些變量不需要更改,是以隻需要放置在隻讀存儲器中即可。

3>已初始化讀寫資料段(rw data)

已初始化資料是在程式中聲明,并且具有初值的變量,這些變量需要占用存儲器的空間,在程式執行時它們需要位于可讀寫的記憶體區域内,并且有初值,以供程式運作時讀寫。

4>未初始化資料段(bbs)

未初始化資料是在程式中聲明,但是沒有初始化的變量,這些變量在程式運作之前不需要占用存儲器的空間。

5>堆(heap)

堆記憶體隻在程式運作時出現,一般由程式員配置設定和釋放。在具有作業系統的情況下,如果程式沒有釋放,作業系統可能在程式(例如一個程序)結束後會後記憶體。

6>棧(statck)

堆記憶體隻在程式運作時出現,在函數内部使用的變量,函數的參數以及傳回值将使用棧空間,棧空間由編譯器自動配置設定和釋放。

c語言程式的存儲區域與const關鍵字的使用方法

3.代碼段、隻讀資料段、讀寫資料段、未初始化資料段屬于靜态區域,而堆和棧屬于動區域。代碼段、隻讀資料段和讀寫資料段将在連接配接之後産生,未初始化資料段将在程式初始化的時候開辟,而對堆和棧将在程式餓運作中配置設定和釋放。

4.c語言程式分為映像和運作時兩種狀态。在編譯-連接配接後形成的映像中,将隻包含代碼段(text)、隻讀資料段(r0 data)和讀寫資料段(rw data)。在程式運作之前,将動态生成未初始化資料段(bss),在程式的運作時還将動态生成堆(heap)區域和棧(stack)區域。

注:1.一般來說,在靜态的映像檔案中,各個部分稱之為節(section),而在運作時的各個部分稱之為段(segment)。如果不詳細區分,統稱為段。

2.c語言在編譯連接配接後,将生成代碼段(text),隻讀資料段(ro data)和讀寫資料段(rw

data)。在運作時,除了上述三個區域外,還包括未初始化資料段(bbs)區域和堆(heap)區域和棧(stack)區域。

1.段的分類

每一個源程式生成的目标代碼将包含源程式所需要表達的所有資訊和功能。目标代碼中各段生成情況如下:

1>代碼段(code)

代碼段由程式中的各個函數産生,函數的每一個語句将最終經過編譯和彙編生成二進制機器代碼

2>隻讀資料段(ro data)

隻讀資料段由程式中所使用的資料産生,該部分資料的特點在運作中不需要改變,是以編譯器會将資料放入隻讀的部分中。c語言的一些文法将生成隻讀資料資料段。

²  隻讀資料段(ro data)

隻讀資料段(ro data)由程式中所使用的資料産生,該部分資料的特點是在運作中不需要改變,是以編譯器會将資料放入隻讀的部分中。以下情況将生成隻讀資料段。

n  隻讀全局變量

定義全局變量const  char a[100]=”abcdefg”将生成大小為100個位元組的隻讀資料區,并使用字元串“abcdefg”初始化。如果定義為const char a[]=”abcdefg”,沒有指定大小,将根據“abcdefgh”字串的長度,生成8個位元組的隻讀資料段。

n  隻讀局部變量

例如:在函數内部定義的變量const char  b[100]=”9876543210”;其初始化的過程和全局變量。

n  程式中使用的常量

例如:在程式中使用printf("information\n”),其中包含了字串常量,編譯器會自動把常量“information \n”放入隻讀資料區。

注:在const  char a[100]={“abcdefg”}中,定義了100個位元組的資料區,但是隻初始化了前面的8個位元組(7個字元和表示結束符的‘\0’)。在這種用法中,實際後面的位元組米有初始化,但是在程式中也不能寫,實際上沒有任何用處。是以,在隻讀資料段中,一般都需要做完全的的初始化。

3.讀寫資料段(rw data)

讀寫資料段表示了在目标檔案中一部分可以讀也可以寫的資料區,在某些場合它們又被稱為已初始化資料段。這部分資料段和代碼,與隻讀資料段一樣都屬于程式中的靜态區域,但是具有科協的特點。

n  已初始化全局變量

例如:在函數外部,定義全局的變量char  a[100]=”abcdefg”

n  已初始化局部靜态變量

例如:在函數中定義static  char b[100]=”9876543210”。函數中由static定義并且已經初始化的資料和數組将被編譯為讀寫資料段。

說明:

讀寫資料區的特點是必須在程式中經過初始化,如果隻有定義,沒有初始值,則不會生成讀寫資料區,而會定義為未初始化資料區(bss)。如果全局變量(函數外部定義的變量)加入static修飾符,寫成static  char a[100]的形式,這表示隻能在檔案内部使用,而不能被其他檔案使用。

4.未初始化資料段(bss)

未初始化資料段常被稱之為bss(英文名為block start by symbol的縮寫)。與讀寫資料段類似,它也屬于靜态資料區。但是該段中資料沒有經過初始化。是以它隻會在目标檔案中被辨別,而不會真正稱為目标檔案中的一個段,該段将會在運作時産生。未初始化資料段隻有在運作的初始化階段才會産生,是以它的大小不會影響目标檔案的大小。

1.在函數體中定義的變量通常是在棧上,不需要在程式中進行管理,由編譯器處理。

2.用malloc,calloc,realoc等配置設定配置設定記憶體的函數所配置設定的記憶體空間在堆上,程式必須保證在使用後使用後freee釋放,否則會發生記憶體洩漏。

3.所有函數體外定義的是全局變量,加了static修飾符後的變量不管在函數内部或者外部存放在全局區(靜态區)。

4.使用const定義的變量将放于程式的隻讀資料區。

在c語言中,可以定義static變量:在函數體内定義的static變量隻能在該函數體内有效;在所有函數體外定義的static變量,也隻能在該檔案中有效,不能在其他源檔案中使用;對于沒有使用static修飾的全局變量,可以在其他的源檔案中使用。這些差別是編譯的概念,即如果不按要求使用變量,編譯器會報錯。使用static 和沒使用static修飾的全局變量最終都将放置在程式的全局去(靜态去)。

c語言中的全局區(靜态區),實際上對應着下述幾個段:

隻讀資料段:r0 data

讀寫資料段:rw data

未初始化資料段:bss data

一般來說,直接定義的全局變量在未初始化資料區,如果該變量有初始化則是在已初始化資料區(rw data),加上const修飾符将放置在隻讀區域(r0 data).

eg:

const char ro[]=”this  is a readonlydata”;//隻讀資料段,不能改變ro數組中的内容,ro存放在隻讀資料段。

 char  rw1[]=”this is global readwrite data”;//已初始化讀寫資料段,可以改變數組rw1中的内容。應為數值是指派不是把”this is  global readwrite data” 位址給了rw1,不能改變”this is global readwrite data”的數值。因為起是文字常量放在隻讀資料段中

char  bss_1[100];//未初始化資料段

const char  *ptrconst = “constant data”;//”constant  data”放在隻讀資料段,不能改變ptrconst中的值,因為其是位址指派。ptrconst指向存放“constant data”的位址,其為隻讀資料段。但可以改變ptrconst位址的數值,因其存放在讀寫資料段中。

int main()

{

         short  b;//b放置在棧上,占用2個位元組

         char   a[100];//需要在棧上開辟100個位元組,a的值是其首位址

chars[]=”abcde”;//s在棧上,占用4個位元組,“abcde”本身放置在隻讀資料存儲區,占6位元組。s是一個位址常量,不能改變其位址數值,即s++是錯誤的。

         char*p1;//p1在棧上,占用4個位元組

         char*p2 =”123456”;//”123456”放置在隻讀資料存儲區,占7個位元組。p2在棧上,p2指向的内容不能更改,但是p2的位址值可以改變,即p2++是對的。

         static  char bss_2[100];//局部未初始化資料段

         static  int   c=0 ;//局部(靜态)初始化區

         p1 = (char *)malloc(10*sizeof(char));//配置設定的記憶體區域在堆區

         strcpy(p1,”xxx”);//”xxx”放置在隻讀資料存儲區,占5個位元組

         free(p1);//使用free釋放p1所指向的記憶體

         return 0;

}

1.隻讀資料段需要包括程式中定義的const型的資料(如:const  char ro[]),還包括程式中需要使用的資料如“123456”。對于const  char ro[]和const char * ptrconst的定義,它們指向的記憶體都位于隻讀資料據區,其指向的内容都不允許修改。差別在于前者不允許在程式中修改ro的值,後者允許在程式中修改ptrconst本身的值。對于後者,改寫成以下的形式,将不允許在程式中修改ptrconst本身的值:

                   const  char * const ptrconst =  “const  data”;

2.讀寫資料段包含了已經初始化的全局變量static  char rw1[]以及局部靜态變量static char

rw2[]。rw1和rw2的差别在于編譯時,是在函數内部使用的還是可以在整個檔案中使用。對于前者,static修飾在于控制程式的其他檔案時候可以通路rw1變量,如果有static修飾,将不能在其他的c語言源檔案中使用rw1,這種影響針對編譯-連接配接的特性,但無論有static,變量rw1都将被放置在讀寫資料段。對于後者rw2,它是局部的靜态變量,放置在讀寫資料區;如果不使用static修飾,其意義将完全改變,它将會是開辟在棧空間局部變量,而不是靜态變量。

3.未初始化資料段,事例1中的bss_1[100]和bss_2[200]在程式中代表未初始化的資料段。其差別在于前者是全局的變量,在所有檔案中都可以使用;後者是局部的變量,隻在函數内部使用。未初始化資料段不設定後面的初始化數值,是以必須使用數值指定區域的大小,

編譯器将根據大小設定bbs中需要增加的長度。

4.棧空間包括函數中内部使用的變量如short b和char  a[100],以及char *p1中p1這個變量的值。

1》變量p1指向的記憶體建立在堆空間上,堆空間隻能在程式内部使用,但是堆空間(例如p1指向的記憶體)可以作為傳回值傳遞給其他函數處理。

2》棧空間主要用于以下3類資料的存儲:

a.函數内部的動态變量

b.函數的參數

c.函數的傳回值

3》棧空間主要的用處是供函數内部的動态變量使用,變量的空間在函數開始之前開辟,在函數退出後由編譯器自動回收

4.看一個事例:

#include<stdio.h>

int main()

         char*p =”tiger”;

         p[1]=’i’;

         p++;

         printf(“%s\n”,p);

編譯後提示:段錯誤

分析:

char *p =”tiger”;系統在棧上開辟了4個位元組存儲p的數值。”tiger”在隻讀存儲區中存儲,是以”tiger”的内容不能改變,*p=”tiger”,表示位址指派,是以,p指向了隻讀存儲區,是以改變p指向的内容會引起段錯誤。但是因為p是存放在棧上,是以p的數值是可以改變的,是以p++是正确的。

1.前言:

const是一個c語言的關鍵字,它限定一個變量不允許被改變。使用const在一定程式上可以提高程式的健壯性,另外,在觀看别人代碼的時候,清晰了解const所起的作用,對了解被人的程式有所幫助。

2.const變量和 常量

(1)const修飾的變量,其值存放在隻讀資料段中,起值不能被改變。稱為隻讀變量。

其形式為 const  int a=5;此處可以用a代替5.

(2)常量:其也存在隻讀資料段中,其數值也不能被改變。其形式為”abc”,5.

3.const 變量和const限定的内容

先看一個事例:

typedef   char *pstr;

intmain()

         char       string[6] = “tiger”;

         const       char*p1 = string;

         const   pstr  p2 = string;

         p1++;

         p2++;

         printf(“p1=%s\np2=%s\n”,p1,p2);

程式經過編譯後,提示錯誤為

error:increment  of read-only  variable ‘p2’

1>const 使用的基本形式為:const char m;

限定m 不可變

2>替換1式中的m,const char *pm;

限定*pm不可變,當然pm是可變的,是以p1++是對的。

3>替換1式中的char,const newtype m;

限定m不可變,問題中的pstr是一種新類型,是以問題中p2不可變,p2++是錯誤的。

(3)const 和指針

類型聲明中const用來修飾一個常量,有如下兩種寫法:

1>const在前面

const  int   nvalue;//nvalue是const

const char  *pcontent;//*pcontent是const,pconst可變

const (char *)pcontent;//pcontent是const,*pcontent可變

char  *const  pcontent;//pcontent是const,*pcontent可變

const char * const  pcontent;//pcontent和*pcontent都是const

2>const 在後面與上面的聲明對等

int const nvalue; // nvalue是const

  char const * pcontent;//*pcontent是const,    pcontent可變

  (char *) constpcontent;//pcontent是const,    *pcontent可變

  char* const pcontent;// pcontent是const,     *pcontent可變

  char const* const pcontent;//pcontent和*pcontent都是const

說明:const和指針一起使用是c語言中一個很常見的困惑之處,下面是兩天規則:

(1)沿着*号劃一條線,如果const位于*的左側,則const就是用來修飾指針所指向的變量,即指針指向為常量;如果const位于*的右側,const就是修飾指針本身,即指針本身是常量。你可以根據這個規則來看上面聲明的實際意義,相信定會一目了然。

(2)對于const (char *) ; 因為char *是一個整體,相當于一個類型(如char),是以,這是限定指針是const。

繼續閱讀