C語言中的const如何保證變數不被修改

2022-09-25 03:18:08 字數 2573 閱讀 5333

這小段文章要理清楚的是,在c語言中,const是如何保證變數不被修改的?

我們可以想到兩種方式:

第一種,由編譯器來阻止修改const變數的語句,讓這種程式不能通過編譯;

第二種,由作業系統來阻止,即把const 的記憶體位址訪問許可權標記為「唯讀」,一旦執行中的程式試圖修改它,就會產生異常,終止程序。

上面想到的這兩種方式,都能達到讓某一變數的值不被修改的目的,那麼究竟是哪一種呢?我們寫兩個例子來看一看。

先來看乙個簡單的例子,原始檔const.c:

#include

const int a=10;

int main()

編譯,會收到乙個 warning:

$ gcc -o const1 const1.c

const.c: in function 『main':

const.c:7:12: warning: initialization discards 『const' qualifier from pointer target type [-wdiscarded-qualifiers]

int *p=&a;

忽略之,執行程式:

$ ./const1

initial: 10

segmentation fault (core dumped)

執行出錯了,報錯是「segmentation fault」,即「段錯誤」,它是在提醒我們,程式中用錯誤的許可權訪問了記憶體某區域。這說明,作業系統把變數$a$載入到了一段唯讀記憶體區域之中,因此對該區域位址的寫操作將引發異常,這是由作業系統的記憶體保護機制決定的。

也就是說,在這段程式裡,const的唯讀屬性是由作業系統來實現的,而不是由編譯器來實現的(編譯器只丟擲了warning,並沒有阻止編譯通過)。

這對嗎?不完全對,我們來看另乙個例子,原始檔const2.c:

#include

int main()

編譯,還是收到同樣的warning:

$ gcc -o const2 const2.c

const.c: in function 『main':

const.c:6:12: warning: initialization discards 『const' qualifier from pointer target type [-wdiscarded-qualifiers]

int *p=&a;

忽略之,執行程式:

./const2

initial: 10

modified: 1

咦?怎麼成功執行了,而且a的值還被順利修改了?

結合以上兩個例子,我們可以得出以下推測:

const只是c語言中的一種對變數的修飾符,例子中的a,與其說是「常量」,不如說是「不打算修改的變數」。它只是語法上的一種宣告,它的作用就是告訴編譯器「我不想修改它」,因此編譯器會從語法上檢查程式中是否有修改它的語句(例如「a=1;」xrkylmttk),一旦發現這種「違背初衷」的語句,就會報錯阻止你。

然而,編譯器所阻止的僅僅是對a這個符號對應值的修改而已,卻並不阻程式設計客棧止對這個位址的值的修改,原始檔「const2.c」之所以能順利通過編譯且正常執行,就是因為它利用乙個名字不叫a的指標指向它,從而繞過了編譯器的語法檢查。

打個比方,周樹人的筆名叫魯迅,警察只知道要抓魯迅,這時候他就可以用一句「你們抓魯迅跟我周樹人有什麼關係?」來騙過他們。

從這個角度來說,const的作用是靠編譯器僅僅從語法檢查來實現的,因此存在執行時的漏洞。

那麼為什麼「const1.c」就不能正常執行呢?

仔細看這兩個源程式,區別僅僅在於,在「const1.c」中,a被宣告為全域性變數,而在「const2.c」中,它被宣告為main函式中的乙個區域性變數。全域性變數與區域性變數的區別在於,前者會在程式開始執行之前就被載入,載入後會一直留在記憶體中,且載入的位置在資料區,直到程式退出;後者只有在執行到它時才會被載入,且載入的位置是執行時的棧幀,一旦超出作用於就會被**。

因此,編譯器會對被宣告為全域性變數的const int a進行優化,把它放程式設計客棧到唯讀記憶體區內,這一記憶體區的許可權是「read\ only」,許可權資訊由作業系統所維護的段表來儲存,程式每訪問某位址時,作業系統都會檢測其訪問許可權是否合法。「const2.c」中企圖用「寫」的方式來訪問「唯讀」的段,自然會報出「segment fault"的錯了。

從這個角度來說,當a是全域性變數時,編譯器把原本只是「不打算修改的變數」優化成了「真正的常量」,然後交給作業系統去維持其不變屬性。

綜上所述,c的初衷只是讓編譯器去保證$const$的不變屬性,這一屬性有漏洞(可以用指標去騙過編譯器修改它),所以當const修飾的物件是全域性變數時(全域性變數很重要,因為很多原始檔都要訪問它,牽一髮而動全身,所以不應輕易更改),編譯器知道自己的能力有限,只能管得了編譯,管不了執行時如何,所以優化了語句把它編**正的常量,讓作業系統的記憶體保護功能來履行這一職責。

這一優化,並不是c規定的,而是編譯器廠程式設計客棧商出於實際應用的考慮作出的選擇。

以上,是我根據編譯器和程式執行時的行為所做的推測,這一思路並不妥當,只是我在程式設計時遇到了上述兩個例子的困惑,又沒找到說得很清楚的資料,所以就寫出來了,若要進一步驗證,應該檢視編譯後的可執行檔案分段情況,我偷了個懶沒看,暫時放在這程式設計客棧裡。

如果推測不正確,希望有前輩指出。

總結

C語言中的const

c語言的中的const,代表的含義是 不可改變的變數 或者可以成為 偽常量 c 中 const,被稱為 乙個有型別描述的常量 const int liv num 10 liv num 18 error 因為liv num 被const修飾不可被直接賦值 那麼,我為什麼說是 偽常量 我們知道在c中給變...

C語言中的const

最近在複習c語言,就來說說關於我以前才開始學c語言很迷糊的乙個知識點 const關鍵字 const關鍵字是用來定義唯讀變數的,說 const 定義的是變數,但又相當於常量 說它定義的是常量,但又有變數的屬性,所以可以叫常變數。用 const 定義常變數的方法很簡單,就是在定義變數時前面加 const...

C語言中的CONST使用

c中的const使用 const是乙個c語言的關鍵字,它限定乙個變數不允許被改變。使用const在一定程度上可以提高程式的安全性和可靠性。const的使用是c語言中乙個比較微妙的地方,請看下面幾個問題。問題 const變數 和 常量 為什麼下面的例子在使用乙個const變數來初始化陣列,ansi c...