C語言的那些小秘密 const修飾符

2021-06-08 02:42:18 字數 4442 閱讀 5372

或許還有不少人對於con

st修飾

符理解的並不深刻,都只是停留在乙個比較淺的層面上,僅僅是在讀別人**的時候看到了const修飾符的使用,自己的寫**的過中從未使用過,所以自然對於con

st修飾

符比較陌生。那麼到底什麼是con

st修飾

符,我們在自己編寫c語言**的過程中又該如何有效的使用con

st修飾

符呢,現在讓我們來學習下con

st修飾

符的使用。

const在c語言中算是乙個比較新的描述符,我們稱之為常量修飾符,即就是說其所修飾的物件為常量。當你**中想要設法阻止乙個變數被改變,那麼這個時候可以選擇使用const關鍵字。在你給乙個變數加上const修飾符的同時,通常需要對它進行初始化,在之後的程式中就不能再去改變它。

可能有的人會有乙個疑問,我們不是有在c中有預處理指令#define variablename variablevalue 可以很方便地進行值替代,幹嘛還要引入const修飾符呢?!這是因為預處理語句雖然可以很方便的進行值得替代,但它有個比較致命的缺點,即預處理語句僅僅只是簡單值替代,缺乏型別的檢測機制。這樣預處理語句就不能享受c編譯器嚴格型別檢查的好處,正是由於這樣,使得它的使用存在著一系列的隱患和侷限性。

在講解const修飾符之前,我們在此首先給出const修飾符的幾個典型作用:

1.   const型別定義:指明變數或物件的值是不能被更新,引入目的是為了取代預編譯指令

2.   可以保護被修飾的東西,防止意外的修改,增強程式的健壯性;

3.   編譯器通常不為普通const常量分配儲存空間,而是將它們儲存在符號表中,這使得它成為乙個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高。

4.    可以節省空間,避免不必要的記憶體分配。

接下來看看具體的使用。

一、const修飾符在函式體內修飾區域性變數。

const int n=5;

和int const n=5;

是等價的。我們在程式設計的過程中一定要清楚的知道const修飾的物件是誰,在這裡修飾的是n,和int沒有關係。const 要求他所修飾的物件為常量,不能被改變,同時也不能夠被賦值,所以下面這樣的寫法是錯誤的。

const int n;

n=0;

對於上面的情況是比較容易理解的,但是當const與指標一起使用時,就容易讓人感到迷惑。例如,下面我們來看看乙個p和q的宣告:

const int *p;

int const *q;

看了上面的**可能有人會覺得 const int *p;表示的是const int型別的指標(const直接修飾int),而 int const *q;表示的是int型別的const指標(const直接修飾指標)。實際上,在上面的宣告中p和q都被宣告為const int型別的指標。而int型別的const指標應該這樣宣告:

int * const r= &n;

以上的p和q都是指向const int型別的指標,也就是說,你在以後的程式裡不能改變*p的值。而r是乙個const指標,它在宣告的時候被初始化指向變數n(即r=&n;)之後,r的值將不再允許被改變,但*r的值可以改變。在此對於判斷const的修飾物件給出一種常使用的方法,我們以*為界線,如果const位於*的左側,則const就是用來修飾指標所指向的變數,即指標指向為常量;如果const位於*的右側,const就是修飾指標本身,即指標本身是常量。

還是給個**來加深下大家的印象吧。

#include

int main(int argc, char* argv)

執行結果如下:

簡單的來分析下吧,因為r指向的是ss的位址,所以修改r指向的位址單元的值的同時ss的值也隨之變化。

結合上述兩種const修飾的情況,我們現在應該可以完成如何宣告乙個指向const int型別的const指標,如下:

const int * const r=&ss;

這個時候我們既不能修改*r的值也不能修改r的值。

接下來看看const用於修飾常量靜態字串,例如:例如:

const char* str="fdsafdsa";

如果沒有const的修飾,我們可能會在後面有意無意的寫str[4]='x'這樣的語句,這樣會導致對唯讀記憶體區域的賦值,然後程式會立刻異常終止。有了const,這個錯誤就能在程式被編譯的時候就立即檢查出來,這就是const的好處。讓邏輯錯誤在編譯期被發現。

二、const在函式宣告時修飾引數

void *memmove( void* dest, const void* src, size_t count ); 這是標準庫中的乙個函式,在標頭檔案#include 中宣告,其功能為由src所指記憶體區域複製count個位元組到dest所指記憶體區域。用於按位元組方式複製字串(記憶體)。它的第乙個引數,是將字串複製到**去(dst),是目的地,這段記憶體區域必須是可寫。它的第二個引數,是要將什麼樣的字串複製出去,我們對這段記憶體區域只做讀取,不寫。於是,我們站在這個函式自己的角度來看,src 這個指標,它所指向的記憶體內所儲存的資料在整個函式執行的過程中是不變。於是src所指向的內容是常量。於是就需要用const修飾。另外需要強調的一點就是src和dest所指記憶體區域可以重疊,但複製後dest內容會被更改。函式返回指向dest的指標。

例如,我們這裡這樣使用它。

#include

#include

int main(int argc, char* argv)

執行結果如下:

如果我們反過來寫,memmove(str,buf,6);那麼編譯器一定會報錯。事實是我們經常會把各種函式的引數順序寫反。事實是編譯器在此時幫了我們大忙。如果編譯器靜悄悄的不報錯,即在函式宣告void *memmove( void* dest, const void* src, size_t count ); 處去掉const即可,那麼這個程式在執行的時候一定會崩潰。這裡還要說明的一點是在函式引數宣告中const一般用來宣告指標而不是變數本身。例如,上面的size_t len,在函式實現的時候可以完全不用更改len的值,那麼是否應該把len也宣告為常量呢?可以,可以這麼做。我們來分析這麼做有什麼優劣。如果加了const,那麼對於這個函式的實現者,可以防止他在實現這個函式的時候修改不需要修改的值(len),這樣很好。但是對於這個函式的使用者,

1.飾符號毫無意義,我們可以傳遞乙個常量整數或者乙個非常量整數過去,反正對方獲得的只是我們傳遞的乙個copy。

2實現。我不需要知道你在實現這個函式的時候是否修改過len的值。

所以,const一般只用來修飾指標。再看乙個複雜的例子

int execv(const char *path, char *const argv);

著重看後面這個,argv.它代表什麼。如果去掉const,我們可以看出char * argv,argv是乙個陣列,它的每個元素都是char *型別的指標。如果加上const.那麼const修飾的是誰呢?修飾的是乙個陣列,argv意思就是說這個陣列的元素是唯讀的。那麼陣列的元素的是什麼型別呢?是char *型別的指

針.也就是說指標是常量,而它指向的資料不是。於是

argv[1]=null; //非法

argv[0][0]='a'; //合法

三、const作為全域性變數

在程式中,我們要盡可能少的使用全域性變數。因為其作用域是全域性,所以程式範圍內都可以修改它的值,從而導致了全域性變數不能保證值的正確性,如果出現錯誤非常難以發現。如果在多執行緒中使用全域性變數,你的程式將會錯的一塌糊塗。多執行緒會修改另乙個執行緒使用的全域性變數的值,如果不注意,一旦出錯後果不堪設想。所以在這種情況下萬不得意不要使用全域性變數。我們要盡可能多的使用const。如果乙個全域性變數只在本檔案中使用,那麼用法和前面所說的函式區域性變數沒有什麼區別。如果它要在多個檔案間共享,那麼就牽扯到乙個儲存型別的問題。

有兩種方式。

1.用extern

例如/* pi.h */

extern const double pi;

/* pi.c */

const double pi=3.14;

然後其他需要使用pi這個變數的,包含pi.h

#include pi.h

或者,自己把那句宣告複製一遍就好。這樣做的結果是,整個程式鏈結完後,所有需要使用pi這個變數的共享乙個儲存區域。

2.使用static,靜態外部儲存類

/* constant.h */

static const double pi=3.14;

需要使用這個變數的*.c檔案中,必須包含這個標頭檔案。前面的static一定不能少。否則鏈結的時候會報告說該變數被多次定義。這樣做的結果是,每個包含了constant.h的*.c檔案,都有乙份該變數自己的copy,該變數實際上還是被定義了多次,占用了多個儲存空間,不過在加了static關鍵字後,解決了檔案間重定義的衝突。壞處是浪費了儲存空間,導致鏈結完後的可執行檔案變大。通常來說,對於儲存空間位元組的變化不是太大的情況下,不是問題。好處是,你不用關心這個變數是在哪個檔案中被初始化的。

下面再來看看一段**:

#include

int main()

為了便於**的閱讀理解,在此就直接在**後面加注釋,就不在這兒做過多的講解了。

C語言的那些小秘密之指標(一)

懂得c語言的人都知道,c語言之所以強大,以及其自由性,絕大部分體現在其靈活的指標運用上。因此,說指標是c語言的靈魂,一點都不為過。所以從我的標題加了個 一 也可以看出指標的重要性,我盡可能的向大家交代清楚我對於指標的理解。所以在講解的過程中我盡可能的用 加文字的描述方式,通過 的分析來加深我們對於指...

C語言的那些小秘密之函式指標

我們經常會聽到這樣的說法,不懂得函式指標就不是真正的c語言高手。我們不管這句話對與否,但是它都從側面反應出了函式指標的重要性,所以我們還是有必要掌握對函式指標的使用。先來看看函式指標的定義吧。函式是由執行語句組成的指令序列或者 這些 的有序集合根據其大小被分配到一定的記憶體空間中,這一片記憶體空間的...

C語言的那些小秘密之函式指標

函式是由執行語句組成的指令序列或者 這些 的有序集合根據其大小被分配到一定的記憶體空間中,這一片記憶體空間的起始位址就成為函式的位址,不同的函式有不同的函式位址,編譯器通過函式名來索引函式的入口位址,為了方便操作型別屬性相同的函式,c c 引入了函式指標,函式指標就是指向 入口位址的指標,是指向函式...