C 程式設計思想學習筆記 第八章 常量

2021-07-02 01:57:00 字數 4353 閱讀 6822

第八章

常量概念(由關鍵字const表示)是為了使程式設計師能夠在變和不變之間畫一條界限。這在c++程式設計專案中提供了安全性和可控性

1、值代替

c語言用#define  bufsize 100的巨集定義方式來做值替換。好處是100的意義清楚,並且修改方便。有經驗的程式設計師會把100加上括號,以防止某些因為邏輯不嚴謹造成的錯誤。但是因為預處理器只做些文字替代,它既沒有型別檢查概念,也沒有該功能,所以這樣的做法經常會產生一些問題。c++中用const值避免這些問題。具體做法是:

const int bufsize = 100;

這樣就可以在編譯時編譯器需要知道這個值得任何地方使用bufsize。同時編譯器還可以執行常量摺疊(constant folding),也就是說,編譯器在編譯時可以通過必要的計算把乙個複雜的常量表示式通過縮減簡單化。這一點在陣列的定義裡顯得尤為重要:

char buf[bufsize];
可以為所有的內建型別以及由他們所定義的變數使用限定符const。

1.1 標頭檔案裡的const

要使用const而非#define,同樣必須把const定義放進標頭檔案。c++的const預設為內部連線(internal linkage),即,const僅在const被定義過的檔案裡才是可見的,而連線時不能被其他編譯單元看到。當定義乙個const時必須給它賦值。除非用extern作出了清楚的說明:

extern const int bufsize;
關於const變數的記憶體空間問題:通常c++編譯器並不為const建立儲存空間,它把這個定義儲存到它的符號表裡。要進行儲存空間分配的情況:1)如上使用了extern 2)取乙個const的位址。因為extern意味著有幾個不同的編譯單元能夠引用它,所以必須有儲存空間。

1.2 const 的安全性

如果用執行期間的產生的值初始化乙個變數而且知道在變數生命期內是不變的,則用const限定改變量是程式設計中的乙個很好的做法。

關於聚合,如果編譯一下**

//c08: constag.cpp

const int i = ;

float f[i[3]];

int main()

會發現第二行,float f[i[3]]報錯。error c2057: 應輸入常量表示式。當定義整型陣列i的時候,實際分配了一塊不能改變的儲存空間。然後不能在編譯期間使用它的值,因為編譯器在編譯期間不需要知道儲存的內容。這樣在定義float型陣列f的時候,i[3]就不是乙個常量或常量表示式,因為不能作為陣列的下標。

1.3 const在c語言和c++中的不同

1)c預設const外部連線,c++預設是內部連線。什麼意思呢?如果你寫以下語句

const int bufsize;

int array[bufsize];

在c語言中,這是合理的。編譯器會認為bufsize是乙個外部變數(預設外部連線),這裡只是乙個宣告而已。

如果在c++中這麼寫,則會報錯,因為編譯器找不到bufsize的定義,它不會去檔案外部尋找bufsize的定義。如果想完成同樣的事情,應該顯式地表達出來,如下

extern const int bufsize;

int array[bufsize];

當然此處的bufsize仍然是乙個宣告,而不是定義。

2)在c++中const不必建立記憶體空間,而在c中,乙個const總是需要建立一塊記憶體空間。

如果僅把const用來作值替換,則不需要建立記憶體空間(如同#define一樣)。

如果對其取了位址,則會建立記憶體或者定義為extern也會建立記憶體。

下面再來區分幾種const的寫法:

extern const int x;    表示x是乙個外部變數,這裡實在引用宣告。

extern const int x = 1;     表示x是在當前定義的乙個變數,並且宣告為extern,可以被其他檔案引用,並且強制分配記憶體

他們的區別僅在於是否初始化。

2、指標

2.1指向const的指標

const int* u;

u是乙個指標,它指向乙個const int。這裡不需要初始化,因為u可以指向任何標示符,該指標本身並不是乙個const,它可以被任意更改,但被指向的位址中的內容是不能更改的。

int const* v;

可能很多人以為它應該是標示乙個指向int的const指標,但實際上它跟const int* u一樣。

2.1.2const指標

int d = 1;

int* const w = &d;

w是乙個指標,這個指標指向int的const指標,它現在不能指向其他地方了,所以必須要初始化。然後該位址中存放的內容是可變的。*w = 2;

再來兩個例子,我們看看到底怎麼區分常指標和常變數。

int d = 1;

const int* const x = &d;  x是乙個常指標,同時它指向乙個常變數

int const* const x2 = &d; x2也是乙個常指標,同時指向乙個常變數

所以看出來怎麼區分了嗎?如果const挨著指標名稱寫,它就是乙個常指標,否則就只是修飾所指向的變數。

注意:可以把乙個非const物件的位址賦給乙個const指標,因為有時不想改變某些可以改變的東西;但是不能把乙個const物件的位址賦給乙個非const指標,因為這樣做可能改變該const物件的值。

3、類3.1類裡的const

考慮這樣一種情況,程式設計師需要像#define一樣定義乙個陣列大小size,他使用了乙個變數const int size; 但是這個資料成員是類裡面的,需要在建構函式裡初始化。而另乙個需要它的陣列array[size]卻並不一定在size之後初始化(雖然這完全可以由程式設計師的細心做到),這樣肯定會出錯,因為此時編譯器並不知道size是什麼。為了防止這種情況,c++使用了乙個叫做「建構函式初始化列表」的東西來優先初始化一些資料,這些賦值操作比任何建構函式體裡面的語句都先執行,而這裡也正是const型別初始化的地方。

class fred

;fred::fred(int sz) : size(sz) {}

void fred::print()

在建構函式後面加乙個冒號,後面跟著資料成員。不難發現,這跟繼承的語法有點相似。可以把size(sz)看成是基類的建構函式,sz是它的引數。實際上,這叫做

內建型別的建構函式。

3.2編譯期間類裡的常量

上面才說了,const量必須在建構函式的初始化列表裡優先初始化,這裡又說有一種const量可以而且必須在定義時賦值。

讓乙個類在編譯期間就有常量,那這個變數必定是屬於整個類的,而不是屬於某個物件的。可以推到出,它必定要用關鍵字static修飾。對之前的**稍加修改

class fred

;

這樣,在編譯期間,size的值就是100。另外,在老的c++**中,static const並不被支援,通常用一種不帶示例的無標記列舉enum來代替,因為乙個列舉在編譯期間必須有值,它在類中區域性出現,而且它的值對於常量表示式是可以使用的。以下**功能相同

class fred

; int array[size];

public:

fred(int sz);

void print();

};

3.3const物件和成員函式

如何定義乙個const成員函式?

必須在宣告和定義中同時加上const修飾符,再次強調,必須同時。

這樣做有什麼用?

乙個const成員函式不管以什麼方式改變乙個非const資料成員變數(const本身無論在何處都不能更改,討論沒有意義)或呼叫非const成員函式,編譯器都會報錯。

class fred

;fred::fred(int ii) : i(ii) {}

int fred::f() const

3.4mutable關鍵字

繼續打臉!上面才說了,const成員函式保證了不能對資料成員進行修改,同時不能呼叫其他非const成員函式。現在問:如果確實需要在const函式裡改變某個變數改怎麼辦?

答案是:關鍵字mutable

class fred

;fred::fred(int ii) : i(ii) {}

int fred::f() const

現在可以對i為所欲為了。類的使用者可從宣告裡看到哪個成員能夠用const成員函式進行修改

3.5volatile關鍵字

與const相反,volatile表示在編譯器的認識範圍以外,這個資料可以被改變

C 程式設計第八章

靜態聯編所支援的多型性稱為編譯時的多型性。動態聯編所支援的多型性稱為執行時的多型性。1 靜態聯編中的賦值相容性及名字支配規律類的物件和呼叫的函式一一對應,編譯時即可確定呼叫關係,從而產生編譯時的多型性。include using namespace std const double pi 3.141...

第八章(筆記)

能在 中進行記憶體單元的定址的暫存器只有4個,分別是bx si di bp 其中bx bp 是基址,bx對應的段位址是ds,bp對應的段位址是ss si di 是變址,單獨使用時段位址是ds,組合使用段位址是跟隨組合的基址對應的段位址 中進行記憶體單元定址彙總 si di bx bp 常量 si 常...

C Primer Plus 學習筆記(第八章)

c primer plus 學習筆記 第八章 c 內聯函式 內聯函式是c 為提高程式執行速度所做的一項改進。常規函式和內聯函式之間的主要區別不在於編寫方式,而在於c 編譯器如何將它們組合到程式中。編譯器將使用相應的函式 替換函式呼叫。對於內聯 程式無需跳到另乙個位置處執行 再跳回來。因此,內聯函式的...