C/C++中const關鍵字詳解

??? 為什么使用const?采用符號常量寫出的代碼更容易維護;指針常常是邊讀邊移動,而不是邊寫邊移動;許多函數參數是只讀不寫的。const最常見用途是作為數組的界和switch分情況標號(也可以用枚舉符代替),分類如下:

? 常變量:??const?類型說明符 變量名

? 常引用:??const?類型說明符 &引用名

? 常對象:??類名?const?對象名

? 常成員函數:??類名::fun(形參)?const

? 常數組:??類型說明符?const?數組名[大小]????

? 常指針:??const?類型說明符* 指針名 ,類型說明符*?const?指針名

首先提示的是:在常變量(const?類型說明符 變量名)、常引用(const?類型說明符 &引用名)、常對象(類名?const?對象名)、?常數組(類型說明符?const?數組名[大小]),?const?與 “類型說明符”或“類名”(其實類名是一種自定義的類型說明符)?的位置可以互換。如:

?????const?int a=5;??int?const?a=5;?等同

???? 類名?const?對象名??const?類名 對象名?等同

用法1:常量
? ??取代了C中的宏定義,聲明時必須進行初始化(!c++類中則不然)。const限制了常量的使用方式,并沒有描述常量應該如何分配。如果編譯器知道了某const的所有使用,它甚至可以不為該const分配空間。最簡單的常見情況就是常量的值在編譯時已知,而且不需要分配存儲。―《C++ Program Language》
??? 用const聲明的變量雖然增加了分配空間,但是可以保證類型安全。
? ? C標準中,const定義的常量是全局的,C++中視聲明位置而定。

用法2:指針和常量
? ? 使用指針時涉及到兩個對象:該指針本身和被它所指的對象。將一個指針的聲明用const“預先固定”將使那個對象而不是使這個指針成為常量。要將指針本身而不是被指對象聲明為常量,必須使用聲明運算符*const。
? ? 所以出現在 * 之前的const是作為基礎類型的一部分:
char *const cp; //到char的const指針
char const *pc1; //到const char的指針
const char *pc2; //到const char的指針(后兩個聲明是等同的)
? ? 從右向左讀的記憶方式:
cp is a const pointer to char.?故pc不能指向別的字符串,但可以修改其指向的字符串的內容
pc2 is a pointer to const char.?故*pc2的內容不可以改變,但pc2可以指向別的字符串

且注意:允許把非 const 對象的地址賦給指向 const 對象的指針,不允許把一個 const 對象的地址賦給一個普通的、非 const 對象的指針。

用法3:const修飾函數傳入參數
? ? 將函數傳入參數聲明為const,以指明使用這種參數僅僅是為了效率的原因,而不是想讓調用函數能夠修改對象的值。同理,將指針參數聲明為const,函數將不修改由這個參數所指的對象。
? ? 通常修飾指針參數和引用參數:
void Fun( const A *in); //修飾指針型傳入參數
void Fun(const A &in); //修飾引用型傳入參數

用法4:修飾函數返回值
? ? 可以阻止用戶修改返回值。返回值也要相應的付給一個常量或常指針。

用法5:const修飾成員函數(c++特性)
const對象只能訪問const成員函數,而非const對象可以訪問任意的成員函數,包括const成員函數;
const對象的成員是不能修改的,而通過指針維護的對象確實可以修改的;
const成員函數不可以修改對象的數據,不管對象是否具有const性質。編譯時以是否修改成員數據為依據進行檢查。

具體展開來講:
(一). 常量與指針

?常量與指針放在一起很容易讓人迷糊。對于常量指針和指針常量也不是所有的學習C/C++的人都能說清除。例如:

??? const int *m1 = new int(10);

??? int* const m2 = new int(20);

在上面的兩個表達式中,最容易讓人迷惑的是const到底是修飾指針還是指針指向的內存區域?其實,只要知道:const只對它左邊的東西起作用,唯一的例外就是const本身就是最左邊的修飾符,那么它才會對右邊的東西起作用。根據這個規則來判斷,m1應該是常量指針(即,不能通過m1來修改它所指向的內容。);而m2應該是指針常量(即,不能讓m2指向其他的內存模塊)。由此可見:

?? 1.?對于常量指針,不能通過該指針來改變所指的內容。即,下面的操作是錯誤的:

????? int i = 10;

????? const int *pi = &i;

????? *pi = 100;

????? 因為你在試圖通過pi改變它所指向的內容。但是,并不是說該內存塊中的內容不能被修改。我們仍然可以通過其他方式去修改其中的值。例如:

????? // 1: 通過i直接修改。

????? i = 100;

????? // 2: 使用另外一個指針來修改。

????? int *p = (int*)pi;

????? *p = 100;

????? 實際上,在將程序載入內存的時候,會有專門的一塊內存區域來存放常量。但是,上面的i本身不是常量,是存放在棧或者堆中的。我們仍然可以修改它的值。而pi不能修改指向的值應該說是編譯器的一個限制。
?? 2. 根據上面const的規則,const int *m1 = new int(10);我們也可寫作:

????? int const *m1 = new int(10);

????? 這是,理由就不須作過多說明了。
?? 3. 在函數參數中指針常量時表示不允許將該指針指向其他內容。

????? void func_02(int* const p)

????? {

????? int *pi = new int(100);

????? //錯誤!P是指針常量。不能對它賦值。

????? p = pi;

????? }

????? int main()

????? {

????? int* p = new int(10);

????? func_02(p);

????? delete p;

????? return 0;

????? }

?? 4. 在函數參數中使用常量指針時表示在函數中不能改變指針所指向的內容。

??? void func(const int *pi)

??? {

??? //錯誤!不能通過pi去改變pi所指向的內容!

??? *pi = 100;

??? }

??? int main()

??? {

??? int* p = new int(10);

??? func(p); 

??? delete p;

??? return 0;

??? }

  我們可以使用這樣的方法來防止函數調用者改變參數的值。但是,這樣的限制是有限的,作為參數調用者,我們也不要試圖去改變參數中的值。因此,下面的操作是在語法上是正確的,但是可能破還參數的值:

??? #include <iostream>

??? #include <string>

??? void func(const int *pi)

??? {

??? //這里相當于重新構建了一個指針,指向相同的內存區域。當然就可以通過該指針修改內存中的值了。

??? int* pp = (int*)pi;

??? *pp = 100;

??? }

??? int main()

??? {

??? using namespace std;

??? int* p = new int(10);

??? cout << "*p = " << *p << endl;

??? func(p);

??? cout << "*p = " << *p << endl;

??? delete p;

??? return 0;

??? }

(二):常量與引用

??? 常量與引用的關系稍微簡單一點。因為引用就是另一個變量的別名,它本身就是一個常量。也就是說不能再讓一個引用成為另外一個變量的別名,?那么他們只剩下代表的內存區域是否可變。即:

??? int i = 10;

??? // 正確:表示不能通過該引用去修改對應的內存的內容。

????const int& ri = i;

??? // 錯誤!不能這樣寫。

??? int& const rci = i;

??? 由此可見,如果我們不希望函數的調用者改變參數的值。最可靠的方法應該是使用引用。下面的操作會存在編譯錯誤:

??? void func(const int& i)

??? {

??? // 錯誤!不能通過i去改變它所代表的內存區域。

??? i = 100;

??? }

??? int main()

??? {

??? int i = 10;

??? func(i);

??? return 0;

??? }

??? 這里已經明白了常量與指針以及常量與引用的關系。但是,有必要深入的說明以下。在系統加載程序的時候,系統會將內存分為4個區域:堆區 棧區全局區(靜態)和代碼區。從這里可以看出,對于常量來說,系統沒有劃定專門的區域來保護其中的數據不能被更改。也就是說,使用常量的方式對數據進行保護是通過編譯器作語法限制來實現的。我們仍然可以繞過編譯器的限制去修改被定義為“常量”的內存區域。看下面的代碼:

??? const int i = 10;

??? // 這里i已經被定義為常量,但是我們仍然可以通過另外的方式去修改它的值。

??? // 這說明把i定義為常量,實際上是防止通過i去修改所代表的內存。

??? int *pi = (int*) &i;

(三):常量函數

??? 常量函數是C++對常量的一個擴展,它很好的確保了C++中類的封裝性。在C++中,為了防止類的數據成員被非法訪問,將類的成員函數分成了兩類,一類是常量成員函數(也被稱為觀察著);另一類是非常量成員函數(也被成為變異者)。在一個函數的簽名后面加上關鍵字const后該函數就成了常量函數。對于常量函數,最關鍵的不同是編譯器不允許其修改類的數據成員。例如:

??? class Test

??? {

??? public:

??? void func() const;

??? private:

??? int intValue;

??? };

??? void Test::func() const

??? {

??? intValue = 100;

??? }

??? 上面的代碼中,常量函數func函數內試圖去改變數據成員intValue的值,因此將在編譯的時候引發異常

??? 當然,對于非常量的成員函數,我們可以根據需要讀取或修改數據成員的值。但是,這要依賴調用函數的對象是否是常量。通常,如果我們把一個類定義為常量,我們的本意是希望他的狀態(數據成員)不會被改變。那么,如果一個常量的對象調用它的非常量函數會產生什么后果呢?看下面的代碼:

??? class Fred{

??? public:

??? void inspect() const;

??? void mutate();

??? };

??? void UserCode(Fred& changeable, const Fred& unChangeable)

??? {

??? changeable.inspect(); // 正確,非常量對象可以調用常量函數。

??? changeable.mutate(); // 正確,非常量對象也允許修改調用非常量成員函數修改數據成員。

??? unChangeable.inspect(); // 正確,常量對象只能調用常理函數。因為不希望修改對象狀態。

??? unChangeable.mutate(); // 錯誤!常量對象的狀態不能被修改,而非常量函數存在修改對象狀態的可能

??? }

??? 從上面的代碼可以看出,由于常量對象的狀態不允許被修改,因此,通過常量對象調用非常量函數時將會產生語法錯誤。實際上,我們知道每個成員函數都有一個隱含的指向對象本身的this指針。而常量函數則包含一個this的常量指針。如下:

??? void inspect(const Fred* this) const;

??? void mutate(Fred* this);

???? 也就是說對于常量函數,我們不能通過this指針去修改對象對應的內存塊。但是,在上面我們已經知道,這僅僅是編譯器的限制,我們仍然可以繞過編譯器的限制,去改變對象的狀態。看下面的代碼:

??? class Fred{

??? public:

??? void inspect() const;
??? private:

??? int intValue;

??? };

??? void Fred::inspect() const

??? {

??? cout << "At the beginning. intValue = "<< intValue << endl;

??? // 這里,我們根據this指針重新定義了一個指向同一塊內存地址的指針。

??? // 通過這個新定義的指針,我們仍然可以修改對象的狀態。

??? Fred* pFred = (Fred*)this;

??? pFred->intValue = 50;

??? cout << "Fred::inspect() called. intValue = "<< intValue << endl;

??? }

??? int main()

??? {

??? Fred fred;

??? fred.inspect();

??? return 0;

??? }

??? 上面的代碼說明,只要我們愿意,我們還是可以通過常量函數修改對象的狀態。同理,對于常量對象,我們也可以構造另外一個指向同一塊內存的指針去修改它的狀態。這里就不作過多描述了。

??? 另外,也有這樣的情況,雖然我們可以繞過編譯器的錯誤去修改類的數據成員。但是C++也允許我們在數據成員的定義前面加上mutable,以允許該成員可以在常量函數中被修改。例如:

??? class Fred{

??? public:

??? void inspect() const;

??? private:

??? mutable int intValue;

??? };

??? void Fred::inspect() const

??? {

??? intValue = 100;

??? }

??? 但是,并不是所有的編譯器都支持mutable關鍵字。這個時候我們上面的歪門邪道就有用了。

??? 關于常量函數,還有一個問題是重載。

??? #include <iostream>

??? #include <string>

??? using namespace std;

??? class Fred{

??? public:

??? void func() const;

??? void func();

??? };

??? void Fred::func() const

??? {

??? cout << "const function is called."<< endl;

??? }

??? void Fred::func()

??? {

??? cout << "non-const function is called."<< endl;

??? }

??? void UserCode(Fred& fred, const Fred& cFred)

??? {

??? cout << "fred is non-const object, and the result of fred.func() is:" << endl;

??? fred.func();

??? cout << "cFred is const object, and the result of cFred.func() is:" << endl;

??? cFred.func();

??? }

??? int main()

??? {

??? Fred fred;

??? UserCode(fred, fred);

??? return 0;

??? }

??? 輸出結果為:

??? fred is non-const object, and the result of fred.func() is:

??? non-const function is called.

??? cFred is const object, and the result of cFred.func() is:

??? const function is called.

??? 從上面的輸出結果,我們可以看出。當存在同名同參數和返回值的常量函數和非常量函數時,具體調用哪個函數是根據調用對象是常量對像還是非常量對象來決定的。常量對象調用常量成員;非常量對象調用非常量的成員。

??? 總之,我們需要明白常量函數是為了最大程度的保證對象的安全。通過使用常量函數,我們可以只允許必要的操作去改變對象的狀態,從而防止誤操作對對象狀態的破壞。但是,就像上面看見的一樣,這樣的保護其實是有限的。關鍵還是在于我們開發人員要嚴格的遵守使用規則。另外需要注意的是常量對象不允許調用非常量的函數。這樣的規定雖然很武斷,但如果我們都根據原則去編寫或使用類的話這樣的規定也就完全可以理解了。
(四):常量返回值

???? 很多時候,我們的函數中會返回一個地址或者引用。調用這得到這個返回的地址或者引用后就可以修改所指向或者代表的對象。這個時候如果我們不希望這個函數的調用這修改這個返回的內容,就應該返回一個常量。這應該很好理解,大家可以去試試。

+++++++++++++++++++++++++++++++++++++++

c++ 中const

+++++++++++++++++++++++++++++++++++++++

1. const常量,如const int max = 100;?
優點:const常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查,而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換時可能會產生意料不到的錯誤(邊際效應)
2.? const 修飾類的數據成員。如:
class A
{

??? const int size;

??? …

}

const數據成員只在某個對象生存期內是常量,而對于整個類而言卻是可變的。因為類可以創建多個對象,不同的對象其const數據成員的值可以不同。所以不能在類聲明中初始化const數據成員,因為類的對象未被創建時,編譯器不知道const 數據成員的值是什么。如

class A

{

?const int size = 100;??? //錯誤

?int array[size];???????? //錯誤,未知的size

}
const數據成員的初始化只能在類的構造函數的初始化表中進行。要想建立在整個類中都恒定的常量,應該用類中的枚舉常量來實現。如

class A

{…

?enum {size1=100, size2 = 200 };

int array1[size1];

int array2[size2];

}

枚舉常量不會占用對象的存儲空間,他們在編譯時被全部求值。但是枚舉常量的隱含數據類型是整數,其最大值有限,且不能表示浮點數。

3. const修飾指針的情況,見下式:

int b = 500;
const int* a = &????????????????? [1]
int const *a = &????????????????? [2]
int* const a = &????????????????? [3]
const int* const a = &?????? [4]

如果你能區分出上述四種情況,那么,恭喜你,你已經邁出了可喜的一步。不知道,也沒關系,我們可以參考《Effectivec++》Item21上的做法,如果const位于星號的左側,則const就是用來修飾指針所指向的變量,即指針指向為常量;如果const位于星號的右側,const就是修飾指針本身,即指針本身是常量。因此,[1]和[2]的情況相同,都是指針所指向的內容為常量(const放在變量聲明符的位置無關),這種情況下不允許對內容進行更改操作,如不能*a = 3;[3]為指針本身是常量,而指針所指向的內容不是常量,這種情況下不能對指針本身進行更改操作,如a++是錯誤的;[4]為指針本身和指向的內容均為常量。

4. const的初始化

先看一下const變量初始化的情況
1) 非指針const常量初始化的情況:A b;
const A a = b;

2) 指針const常量初始化的情況:

A* d = new A();
const A* c = d;
或者:const A* c = new A();
3)引用const常量初始化的情況:
A f;
const A& e = f;????? // 這樣作e只能訪問聲明為const的函數,而不能訪問一般的成員函數;

??? [思考1]: 以下的這種賦值方法正確嗎?
??? const A* c=new A();
??? A* e = c;
??? [思考2]: 以下的這種賦值方法正確嗎?
??? A* const c = new A();
??? A* b = c;

5. 另外const 的一些強大的功能在于它在函數聲明中的應用。在一個函數聲明中,const可以修飾函數的返回值,或某個參數;對于成員函數,還可以修飾是整個函數。有如下幾種情況,以下會逐漸的說明用法:A&operator=(const A& a);
void fun0(const A* a );
void fun1( ) const; // fun1( ) 為類成員函數
const A fun2( );

1) 修飾參數的const,如 void fun0(const A* a ); void fun1(const A& a);
調用函數的時候,用相應的變量初始化const常量,則在函數體中,按照const所修飾的部分進行常量化,如形參為const A*a,則不能對傳遞進來的指針的內容進行改變,保護了原指針所指向的內容;如形參為const A&a,則不能對傳遞進來的引用對象進行改變,保護了原對象的屬性。
[注意]:參數const通常用于參數為指針或引用的情況,且只能修飾輸入參數;若輸入參數采用“值傳遞”方式,由于函數將自動產生臨時變量用于復制該參數,該參數本就不需要保護,所以不用const修飾。

[總結]對于非內部數據類型的輸入參數,因該將“值傳遞”的方式改為“const引用傳遞”,目的是為了提高效率。例如,將void Func(A a)改為void Func(const A &a)

??????對于內部數據類型的輸入參數,不要將“值傳遞”的方式改為“const引用傳遞”。否則既達不到提高效率的目的,又降低了函數的可理解性。例如void Func(int x)不應該改為void Func(const int &x)

2)? 修飾返回值的const,如const A fun2( ); const A* fun3( );
這樣聲明了返回值后,const按照"修飾原則"進行修飾,起到相應的保護作用。const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}

返回值用const修飾可以防止允許這樣的操作發生:Rational a,b;
Radional c;
(a*b) = c;

一般用const修飾返回值為對象本身(非引用和指針)的情況多用于二目操作符重載函數并產生新對象的時候。
[總結]

1.? 一般情況下,函數的返回值為某個對象時,如果將其聲明為const時,多用于操作符的重載。通常,不建議用const修飾函數的返回值類型為某個對象或對某個對象引用的情況。原因如下:如果返回值為某個對象為const(const A test = A實例)或某個對象的引用為const(const A& test = A實例),則返回值具有const屬性,則返回實例只能訪問類A中的公有(保護)數據成員和const成員函數,并且不允許對其進行賦值操作,這在一般情況下很少用到。

2.? 如果給采用“指針傳遞”方式的函數返回值加const修飾,那么函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。如:

const char * GetString(void);

如下語句將出現編譯錯誤:

char *str=GetString();

正確的用法是:

const char *str=GetString();

3.???? 函數返回值采用“引用傳遞”的場合不多,這種方式一般只出現在類的賻值函數中,目的是為了實現鏈式表達。如:

class A

{…

?A &operate = (const A &other);? //負值函數

}
A a,b,c;????????????? //a,b,c為A的對象

a=b=c;??????????? //正常

(a=b)=c;????????? //不正常,但是合法

若負值函數的返回值加const修飾,那么該返回值的內容不允許修改,上例中a=b=c依然正確。(a=b)=c就不正確了。
[思考3]: 這樣定義賦值操作符重載函數可以嗎?
const A& operator=(const A& a);

6.???? 類成員函數中const的使用
一般放在函數體后,形如:void fun() const;
任何不會修改數據成員的函數都因該聲明為const類型。如果在編寫const成員函數時,不慎修改了數據成員,或者調用了其他非const成員函數,編譯器將報錯,這大大提高了程序的健壯性。如:

class Stack

{

?public:

????? void Push(int elem);

????? int Pop(void);

????? int GetCount(void) const;?? //const 成員函數

?private:

????? int m_num;

????? int m_data[100];

};

int Stack::GetCount(void) const

{

? ++m_num;????????????? //編譯錯誤,企圖修改數據成員m_num

? Pop();??????????????????? //編譯錯誤,企圖調用非const函數

? Return m_num;

}

7. 使用const的一些建議

1) 要大膽的使用const,這將給你帶來無盡的益處,但前提是你必須搞清楚原委;
2) 要避免最一般的賦值操作錯誤,如將const變量賦值,具體可見思考題;
3) 在參數中使用const應該使用引用或指針,而不是一般的對象實例,原因同上;
4) const在成員函數中的三種用法(參數、返回值、函數)要很好的使用;
5) 不要輕易的將函數的返回值類型定為const;
6) 除了重載操作符外一般不要將返回值類型定為對某個對象的const引用;

[思考題答案]
1) 這種方法不正確,因為聲明指針的目的是為了對其指向的內容進行改變,而聲明的指針e指向的是一個常量,所以不正確;
2) 這種方法正確,因為聲明指針所指向的內容可變;
3) 這種做法不正確;
在const A::operator=(const A& a)中,參數列表中的const的用法正確,而當這樣連續賦值的時侯,問題就出現了:
A a,b,c:
(a=b)=c;
因為a.operator=(b)的返回值是對a的const引用,不能再將c賦值給const常量。

++++++++++++++++++++++++++++++++++++++++

const 在c和c++中的區別??http://tech.e800.com.cn/articles/2009/722/1248229886744_1.html

++++++++++++++++++++++++++++++++++++++++

1. C++中的const正常情況下是看成編譯期的常量,編譯器并不為const分配空間,只是在編譯的時候將期值保存在名字表中,并在適當的時候折合在代碼中.所以,以下代碼:
using namespace std;
int main()
{
const int a = 1;
const int b = 2;
int array[ a + b ] = {0};
for (int i = 0; i < sizeof array / sizeof *array; i++)
{
cout << array << endl;
}
}
在可以通過編譯,并且正常運行.但稍加修改后,放在C編譯器中,便會出現錯誤:
int main()
{
int i;
const int a = 1;
const int b = 2;
int array[ a + b ] = {0};
for (i = 0; i < sizeof array / sizeof *array; i++)
{
printf("%d",array);
}
}
錯誤消息:
c:\test1\te.c(8): error C2057: 應輸入常數表達式
c:\test1\te.c(8): error C2466: 不能分配常數大小為 0 的數組
出現這種情況的原因是:在C中,const是一個不能被改變的普通變量,既然是變量,就要占用存儲空間,所以編譯器不知道編譯時的值.而且,數組定義時的下標必須為常量.
2. 在C語言中: const int size; 這個語句是正確的,因為它被C編譯器看作一個聲明,指明在別的地方分配存儲空間.但在C++中這樣寫是不正確的.C++中const默認是內部連接,如果想在C++中達到以上的效果,必須要用extern關鍵字.即C++中,const默認使用內部連接.而C中使用外部連接.
(1) 內連接:編譯器只對正被編譯的文件創建存儲空間,別的文件可以使用相同的表示符或全局變量.C/C++中內連接使用static關鍵字指定.
(2) 外連接:所有被編譯過的文件創建一片單獨存儲空間.一旦空間被創建,連接器必須解決對這片存儲空間的引用.全局變量和函數使用外部連接.通過extern關鍵字聲明,可以從其他文件訪問相應的變量和函數.
/* C++代碼??header.h?*/
const int test = 1;
/* C++代碼??test1.cpp?*/
#include "header.h"
using namespace std;
int main() { cout << "in test1 :" << test << endl; }
/* C++代碼?test2.cpp */
#include "header.h"
using namespace std;
void print() { cout << "in test2:" << test << endl;}
以上代碼編譯連接完全不會出問題,但如果把header.h改為:
extern const int test = 1;
在連接的時候,便會出現以下錯誤信息:
test2 error LNK2005: "int const test" (?test@@3HB) 已經在 test1.obj 中定義
??? 因為extern關鍵字告訴C++編譯器test會在其他地方引用,所以,C++編譯器就會為test創建存儲空間,不再是簡單的存儲在名字表里面.所以,當兩個文件同時包含header.h的時候,會發生名字上的沖突.
此種情況和C中const含義相似:
/* C代碼 header.h */
const int test = 1;
/* C代碼?test1.c */
#include "header.h"
int main() { printf("in test1:%d\n",test); }
/* C代碼?test2.c */
#include "header.h"
void print() { printf("in test2:%d\n",test); }
錯誤消息:
test3 fatal error LNK1169: 找到一個或多個多重定義的符號
test3 error LNK2005: _test 已經在 test1.obj 中定義

也就是說:在c++ 中const 對象默認為文件的局部變量。與其他變量不同,除非特別說明,在全局作用域聲明的 const 變量是定義該對象的文件的局部變量。此變量只存在于那個文件中,不能被其他文件訪問。通過指定 const 變更為 extern,就可以在整個程序中訪問 const 對象:
????? // file_1.cc
????? // defines and initializes a const that is accessible to other files
????? extern const int bufSize = fcn();
????? // file_2.cc
????? extern const int bufSize; // uses bufSize from file_1
????? // uses bufSize defined in file_1
????? for (int index = 0; index != bufSize; ++index)
??????????? // ...

3. C++中,是否為const分配空間要看具體情況.如果加上關鍵字extern或者取const變量地址,則編譯器就要為const分配存儲空間.
4. C++中定義常量的時候不再采用define,因為define只做簡單的宏替換,并不提供類型檢查.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37399.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37399.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37399.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

音視頻 vs2017配置FFmpeg

vs2017 ffmpeg4.2.1 一、首先我把FFmpeg整理了一下&#xff0c;放在C盤 二、新建空項目 三、添加main.cpp&#xff0c;將bin文件夾下dll文件拷貝到cpp目錄下 #include<stdio.h> #include<iostream>extern "C" { #include "libavcodec/avcodec.h&…

【Docker】使用 Docker Registry 搭建自己的 Docker 鏡像倉庫

使用 Docker Registry 搭建自己的 Docker 鏡像倉庫 在使用 Docker 進行應用程序的開發和部署時&#xff0c;使用 Docker 鏡像倉庫是一個很好的實踐。它允許集中存儲和管理 Docker 鏡像&#xff0c;方便團隊協作和版本控制。在本文中&#xff0c;將介紹如何使用 Docker Registr…

Nginx隨筆

Nginx下載鏈接 安裝命令&#xff1a; apt update apt install nginx 一、基礎命令&#xff08;Ubuntu&#xff09; 1、在全局 nginx -t //檢查Nginx的配置文件是否有錯 systemctl start nginx //啟動Nginx systemctl stop nginx //停止Nginx systemctl status nginx //查…

【數據結構與算法——TypeScript】圖結構(Graph)

【數據結構與算法——TypeScript】 圖結構(Graph) 認識圖結構以及特性 什么是圖? 在計算機程序設計中&#xff0c;圖結構 也是一種非常常見的數據結構。 但是&#xff0c;圖論其實是一個非常大的話題 認識一下關于圖的一些內容 圖的抽象數據類型一些算法實現。 什么是圖?…

jmeter獲取mysql數據

JDBC Connection Configuration Database URL: jdbc:mysql:// 數據庫地址 /庫名 JDBC Driver class&#xff1a;com.mysql.jdbc.Driver Username&#xff1a;賬號 Password&#xff1a;密碼 JDBC Request 字段含義 字段含義 Variable Name Bound to Pool 數據庫連接池配置…

使用vue3 + ts + vite + v-md-editor 在前端頁面預覽markdown文件

1.效果預覽 2. 依賴包安裝 yarn add kangc/v-md-editornext v-md-editor中文官網&#xff1a;https://code-farmer-i.github.io/vue-markdown-editor/zh/ v-md-editor分為4種組件&#xff1a; 輕量版編輯器進階版編輯器預覽組件html預覽組件 對UI組件庫頁面&#xff0c;我只需…

問道管理:縮量小幅上漲說明什么?

股市里面&#xff0c;股票價格上漲或跌落都是常見現象。可是關于那些在商場上尋求收益的出資者來說&#xff0c;他們需要對每一個股市中的價格動搖有深化的了解&#xff0c;以便做出更正確的出資決策。最近&#xff0c;出資者們發現商場縮量小幅上漲的現象時有發生&#xff0c;…

Jmeter壓測實戰:Jmeter二次開發之自定義函數

目錄 1 前言 2 開發準備 3 自定義函數核心實現 3.1 新建項目 3.2 繼承實現AbstractFunction類 3.3 最終項目結構 4 Jmeter加載擴展包 4.1 maven構建配置 4.2 項目打包 4.3 Jmeter加載擴展包 5 自定義函數調用調試 5.1 打開Jmeter函數助手&#xff0c;選擇自定義函數…

clickhouse 刪除操作

OLAP 數據庫設計的宗旨在于分析適合一次插入多次查詢的業務場景&#xff0c;市面上成熟的 AP 數據庫在更新和刪除操作上支持的均不是很好&#xff0c;當然 clickhouse 也不例外。但是不友好不代表不支持&#xff0c;本文主要介紹在 clickhouse 中如何實現數據的刪除&#xff0c…

單鏈表相關操作(插入,刪除,查找)

通過上一節我們知道順序表的優點&#xff1a; 可隨機存儲&#xff08;O(1)&#xff09;&#xff1a;查找速度快 存儲密度高&#xff1a;每個結點只存放數據元素&#xff0c;而單鏈表除了存放數據元素之外&#xff0c;還需存儲指向下一個節點的指針 http://t.csdn.cn/p7OQf …

【2023年11月第四版教材】《第4章-信息系統管理(合集篇)》

第4章-信息系統管理之管理方法&#xff08;第四版新增章節&#xff09;&#xff08;第一部分&#xff09; 章節說明1 管理方法1.1 信息系統四個要素1.2 信息系統四大領域1.3 信息系統戰略三角1.4 信息系統架構轉換1.5 信息系統體系架構1.6 信息系統運行1.7 運行和監控1.8 管理和…

北郵鄧中亮:深度融合5G+北斗,實現高精準定位

如今&#xff0c;萬物互聯時代&#xff0c;物與物、物與人、人與人之間需要實現更多的互聯。在如此復雜多變的環境中&#xff0c;定位技術面臨著著更多挑戰和需求&#xff0c;需要不斷的創新和改進。唯有如此&#xff0c;才能滿足未來智能交通、無人駕駛和工業互聯網等領域的高…

kafka基本概念及操作

kafka介紹 Kafka是最初由Linkedin公司開發&#xff0c;是一個分布式、支持分區的&#xff08;partition&#xff09;、多副本的 &#xff08;replica&#xff09;&#xff0c;基于zookeeper協調的分布式消息系統&#xff0c;它的最大的特性就是可以實時的處理大量數據以滿足各…

【LeetCode】242 . 有效的字母異位詞

242 . 有效的字母異位詞&#xff08;簡單&#xff09; 方法&#xff1a;哈希表 思路 首先判斷兩個字符串長度是否相等&#xff0c;不相等直接返回 false&#xff1b;接下來設置一個長度為26 的哈希表&#xff0c;分別對應26個小寫字母&#xff1b;遍歷兩個字符串&#xff0c;…

Go語言工程實踐之測試與Gin項目實踐

Go 語言并發編程 及 進階與依賴管理_軟工菜雞的博客-CSDN博客 03 測試 回歸測試一般是QA(質量保證)同學手動通過終端回歸一些固定的主流程場景 集成測試是對系統功能維度做測試驗證,通過服務暴露的某個接口,進行自動化測試 而單元測試開發階段&#xff0c;開發者對單獨的函數…

day-21 代碼隨想錄算法訓練營(19)二叉樹part07

530.二叉搜索樹的最小絕對差 思路一&#xff1a;二叉搜索樹的中序遍歷必為升序數組&#xff0c;加入數組后計算相鄰兩個數差值&#xff0c;即可求出最小絕對差 思路二&#xff1a;同樣的思路&#xff0c;中序遍歷&#xff0c;直接使用指針記錄上一個節點&#xff0c;同時更新…

KAFKA第二課之生產者(面試重點)

生產者學習 1.1 生產者消息發送流程 在消息發送的過程中&#xff0c;涉及到了兩個線程——main線程和Sender線程。在main線程中創建了一個雙端隊列RecordAccumulator。main線程將消息發送給RecordAccumulator&#xff0c;Sender線程不斷從RecordAccumulator中拉取消息發送到K…

03-基礎入門-搭建安全拓展

基礎入門-搭建安全拓展 1、涉及的知識點2、常見的問題3、web權限的設置4、演示案例-環境搭建&#xff08;1&#xff09;PHPinfo&#xff08;2&#xff09;wordpress&#xff08;3&#xff09;win7虛擬機上使用iis搭建網站&#xff08;4&#xff09;Windows Server 2003配置WEB站…

C#應用處理傳入參數 - 開源研究系列文章

今天介紹關于C#的程序傳入參數的處理例子。 程序的傳入參數應用比較普遍&#xff0c;特別是一個隨操作系統啟動的程序&#xff0c;需要設置程序啟動的時候不顯示主窗體&#xff0c;而是在后臺運行&#xff0c;于是就有了傳入參數問題&#xff0c;比如傳入/h或者/min等等。所以此…

YOLO v8目標跟蹤詳細解讀(二)

上一篇&#xff0c;結合代碼&#xff0c;我們詳細的介紹了YOLOV8目標跟蹤的Pipeline。大家應該對跟蹤的流程有了大致的了解&#xff0c;下面我們將對跟蹤中出現的卡爾曼濾波進行解讀。 1.卡爾曼濾波器介紹 卡爾曼濾波&#xff08;kalman Filtering&#xff09;是一種利用線性…