VLA就是variable-length array,也就是變長數組。數組
最近寫程序的時候無心間發現,gcc中居然支持下面這種寫法:函數
int n = 10;spa
int a[n];3d
注意上面的語句是在函數內部寫的,也就是n和a都是自動變量。指針
當時十分疑惑,C語言中數組的長度不該該是常量或常量表達式嗎?爲何變量也能夠。我將代碼在VC中跑了一下,發現編譯出錯,提示數組的大小未知,說明VC中是不支持VLA的。code
那既然有的編譯器支持VLA,又有的編譯器不支持VLA,那麼C標準究竟是怎樣規定的呢?而後我看是看書、在網上查資料。對象
C Primer Plus一書中是這樣描述的:blog
C90標準中並不支持VLA,C99開始支持VLA,很大的一個緣由:FORTRAN中支持這種寫法。C99中對對VLA有一些限制,好比變長數組必須是自動存儲類型,也就是說,若是我上面兩句放在函數外面就就不能經過編譯了,這是由於在函數外面定義的是全局變量,此外,使用VLA不能對數組進行初始化,由於它的長度在運行時才能肯定。內存
此外VLA並非真正的變長,它實際上只是將數組的長度推遲到運行時肯定而已,也就是說C90標準中,數組的長度必須在編譯時期就知道,但C99支持VLA後,數組的長度能夠推遲到運行時知道,可是,一旦長度肯定,數組的長度就不能變了。編譯器
此外,網上的大神說,C++的標準中不管是C++90仍是C++99仍是C++11都不支持VLA的這種寫法。
鑑於以上緣由,在C語言中,若是想用變長的數組,仍是老老實實用malloc分配吧,在C++中固然有更好的選擇,就是vector,固然C++11中又推出了一個array,並且這兩種都是真正的變長,也就是數組的長度隨時均可以改變。
const關鍵字最先是C++中的產物,後來才引入到C語言中。const在C語言中和在C++中是至關不同的。
在C語言中const修飾的,被認爲是一個只讀的、或者叫不可改變的變量,它實際上只是一個變量,只不過對這個變量作了一些限制。而C++中const修飾的纔是真正的常量。固然這其中還有不少細節的地方,有些地方我也還有些模糊,下面,我就經過例子,把本身已經理解的東西寫出來。
const int a = 10; int array[a]; int main() { return 0; }
用gcc和g++編譯的結果分別以下:
能夠看到gcc下不能經過編譯,可是g++下能夠經過,說明C語言中有錯,在C++中沒錯。
緣由解釋:
首先說明,即便在支持VLA的編譯器下,(個人gcc是支持的),前面提到了VLA數組是有限制的,VLA必須是自動存儲類型,而上面的代碼中數組是全局變量,因此並不存在是否支持VLA的問題。上面提到,在C語言中const被認爲是一個受到必定限制的變量,是變量就要被分配數據區(或者運行時的棧區等)內存空間,因爲a是全局變量,全局變量位於數據區,空間在編譯時期就分配了。而,須要注意,編譯時期,編譯器是不能讀取數據區的內存的(它能夠分配數據區的內存,並初始化內存,可是不能從數據區的牛叉女內存中讀取數據)。因此在編譯時期,編譯器其實並不知道a的值是什麼,由於它不能讀數據區的內存而a的值是在內存中的。可是,對於數組array編譯器是必定要知道數組的長度才行的,也就是必需要知道a的值,這樣就矛盾了,因此編譯器就報錯了!
緣由就是C++真的把const當成常量看待。
詳細解釋一下:
const int a = 10;這條語句中10是咱們所說的字面量,不管是在C中仍是在C++中字面量都是保存在代碼段中,編譯初期會將其保存在符號表中。C++儘可能不對const分配數據區(或者運行時的棧區)的內存空間,只在必須分配內存時才分配(這個後面再說)。下面一條語句int array[a],編譯器必定要知道a的值的,C語言要想知道a的值,必須讀內存,可是C++卻不須要,直接讀取代碼段中的的符號表便可,編譯時期訪問符號表是沒有任何問題的,可是訪問數據區的內存是作不到的。因此上面的語句在C++中是沒有問題的。
再來講說,什麼叫儘量不爲const分配內存。
若是代碼是這樣
const int a = 10; const int *p = &a; int array[a]; int main() { return 0; }
注意 const int *p = &a;這句,對a取地址操做,咱們知道位於代碼段的數據是不取地址的,因此這個時候,只能給a在數據區分配空間了。
因而就出現了新的問題,既然給a分配了數據區的空間,那是否是編譯時期就不知道a的值了,由於畢竟編譯時期是不能讀取數據區的內存的,那麼後面數組的定義也就不行了吧?可是答案卻相反,依然能夠,這是由於當編譯器讀a的值的時候,不是從數據區的內存中,而是程序段的符號表中讀取的那個字面常量10。因此在編譯實際依然可以肯定數組的長度。
#include <stdio.h> int main() { const int a = 10; int *pa = (int *)&a; printf("pa指向的地址爲:%p a的地址爲:%p\n",pa,&a); (*pa)++; printf("a = %d,*pa = %d\n",a,*pa); return 0; }
咱們分別用gcc和g++編譯他,而後分別看結果,以下:
驚奇地發現,雖然都能順利經過編譯,可是C的執行和C++的執行居然不同!
好吧,下面解釋緣由。
仍是要聲明一下,C語言中const就是一個值不能改變的變量,就是個受限制的變量,可是,咱們雖然咱們不能經過a修改那塊內存的值,可是咱們能夠經過指針間接去修改。這裏要注意那個強制類型轉換,若是不寫強制類型轉換,編譯器就會報錯,是容許將const int *賦值給int*的。在C++中,這一點和C是同樣的,就是雖然咱們不能經過a自己修改那塊內存的值,可是咱們能夠經過指針間接去修改。可是爲何C和C++中的輸出不同呢?緣由就是C++在讀a的時候,實際上是去代碼段中讀字面常量10去了,而C是讀a所標識的那塊棧區的內存。其實a所標識的內存的內容都已經變成11了,不管是C仍是C++都是同樣,區別就在於C讀const數據和讀普通變量同樣,都是從數據段(若是是局部變量就是從棧區)讀取數組,而C++倒是讀取代碼段的字面常量!(間接修改const的時候,固然都是修改的數據區或棧區的內存,而不是代碼段,由於代碼段是隻讀的)
因此C++中const修飾的能夠認爲就是常量!可是C語言中卻不能這麼認爲。
其實經過上面的分析,咱們應該能夠得出一個結論:C++中的const之因此和C語言中的const不同,C++中的const之因此可以當作常量,就是由於C++在讀取const的時候,實際上讀取的是代碼段的字面常量,而不是數據區(對於全局變量來講是靜態區,對於局部變量來講是棧區)的內存中的數值。
看下面代碼:
#include <stdio.h> int main() { int i = 10; const int a = i; int *pa = (int *)&a; printf("pa指向的地址爲:%p a的地址爲:%p\n",pa,&a); (*pa)++; printf("a = %d,*pa = %d\n",a,*pa); return 0; }
幾乎仍是一樣的代碼,只是先把字面常量10賦值給了變量i,而後用i初始化const int a,可是咱們發現,在C++中,執行結果卻變了。
爲何呢?前面強調了,C++讀取const的值,其實是讀取代碼段的字面常量,那麼,若是咱們初始化const的時候,給它的不是字面量(或者是常量表達式),那麼他就沒有字面量能夠讀啦!這時候就只能退而求其次,去讀數據區內存中的值啦!這個時候,C++中的const和C語言中的const就同樣了。
須要注意的是 sizeof是C和C++中的運算符,並且他的值一般都是在編譯時肯定的,能夠認爲是一個字面常量。
好比:
#include <stdio.h> int main() { const int a = sizeof(int); int *pa = (int *)&a; printf("pa指向的地址爲:%p a的地址爲:%p\n",pa,&a); (*pa)++; printf("a = %d,*pa = %d\n",a,*pa); return 0; }
好比:
#include <stdio.h> class A{ public: const int a; int array[a]; A(int i):a(i) { } }; int main() { return 0; }
編譯器會報錯:
首先,類只是定義類型的地方,不能再類中初始化成員變量,因此在類定義中寫const int a = 10是不對的,const成員的初始化要放在初始化列表中。咱們知道,在對象建立以前纔會調用構造函數進行對象的初始化,因此在編譯時期咱們根本就不知道a的值。因此把a當作數組的長度是有問題的。
咱們能夠這樣:
#include <stdio.h> class A{ public: static const int a = 10; int array[a]; A() { } }; int main() { return 0; }
這樣定義的a就能夠當成一個常量來使用了。注意的是,這時候,a的語句不是聲明瞭,而是定義+初始化,並且C++只容許這種狀況能夠在類內初始化,就是當變量被 static 和const修飾,且爲int類型時。(固然這種狀況依然能夠在類外定義和初始化,只不事後面就不能用a定義數組的長度了,也會提示數組的長度不肯定)
C語言中const就是一個受限制的變量,讀取const時是從數據區的內存讀取的(全局從靜態去,局部從棧區),能夠用指針間接修改其標識的數據區的內存區域,在讀取const的值時,也是讀取它標識的數據區的內存中的值。
在C++中,const大多數狀況下能夠當成常量來使用,這是由於,雖然C++也會爲const在數據區開闢內存(C++儘可能不這樣左),咱們也能夠經過指針(或者很是量的引用)來簡介修改其標識的數據區的內存區域的值,可是,在讀取const時,不是讀取數據區的內存區域中的值(也頗有可能根本就沒有分配內存),而是讀取代碼段的字面常量。因此能夠達到常量的效果。
最後要警戒C++中const退化成C語言中的const,有兩種狀況,一種是初始化的const的時候是用變量初始化的,而不是字面常量(或常量表達式)。第二種狀況就是const修飾類中成員變量的時候。
給一個建議:若是咱們想用const定義常量,那麼咱們就要用字面常量或常量表達式初始化const,並且要將const用static修飾(注意在C++中若是定義的是全局的const,默認爲static修飾,可是在類中並非這樣的,這也是爲何咱們在類中定義常量要用static修飾)。在類中定義常量的時候要同時用cosnt和static修飾,並且儘可能在類的內部進行初始化。
若是你以爲對你有用,請贊一個吧