1 C中的const
C中const
修飾的變數是隻讀變數,在使用const
關鍵字宣告定義變數時會給該變數分配記憶體空間。
const
修飾的全域性變數預設是外部連結的,即其它原始檔可以直接使用該變數。
const
修飾的區域性變數儲存在棧區中,不能通過變數名直接修改該變數的值,但是可以通過指標的方式修改該變數對應的值,從某種意義上來說,C中const
修飾的變數不是真正意義上的常量,可以將其當作一種只讀變數。
C中const示例:
// fun.c
// c中const修飾的全域性變數預設是外部連結的
const int num = 100; // a的本質是隻讀的變數,存放在文字常量區(記憶體空間只讀)
// main.c
#include <stdio.h>
// 對fun.c中的a進行宣告(不要賦值)
extern const int num;
void test()
{
printf("num = %d\n", num);
// num = 200; // error: assignment of read-only variable 'num'
}
int main(int argc, char *argv[])
{
test();
return 0;
}
#include <stdio.h>
void test()
{
const int data = 200; // 區域性只讀變數,通過指標的形式修改data變數的值
printf("data = %d\n", data); // 200
int* p = (int*)&data;
*p = 300;
printf("data = %d\n", data); // 300
}
int main(int argc, char *argv[])
{
test();
return 0;
}
總結:
-
const
修飾全域性變數時,該變數儲存在文字常量區(只讀),不能通過指標的方式修改其內容 -
const
修飾區域性變數是,該變數儲存在棧區中(可讀可寫),通過指標的方式可以修改其內同
2 C++中的const
C++中const
修飾的變數不需要建立記憶體空間,比如定義常量const int data = 10;
,C++會在一張符號表中新增name
為data
,value
為10的一條記錄,如下圖所示:
既然,const
修飾的變數沒有記憶體空間,所以在C++中const
修飾的變數才是真正意義上的常量。
C++中定義宣告的全域性常量是內部連結的,只能作用於當前的整個檔案中,如果想讓其它原始檔對該常量進行訪問的,必須加extern
關鍵字將該常量轉換成外部連結。
在C++中,是否為const
常量分配記憶體空間依賴於如何使用,以下情況會對常量進行記憶體空間的分配:
- 對
const
常量取地址時 - 使用變數的形式初始化
const
修飾的變數時 const
修飾自定義資料型別(結構體、物件)時
C++中const示例:
// fun.c
// const修飾的全域性變數,預設時內部連結,不能直接被其它原始檔使用
//const int num = 100;
// 可以通過將num轉換成外部連結的方式,供其它原始檔對num的訪問
extern const int num = 100;
// main.c
#include <iostream>
using namespace std;
extern const int num;
struct Person{
int num;
char name[32];
};
void test()
{
cout << "全域性num = " << num << endl; // error: undefined reference to `num'
// 1. c++中對const修飾的基礎型別的變數不會開闢記憶體空間,只是將其放到符號表中
const int data = 100;
// data = 200; // error: 只讀
cout << "data = " << data << endl;
// 2. 對data取地址時,系統會給data開闢空間
int* p = (int*)&data;
*p = 2000;
cout << "*p = " << *p << endl; // 2000
cout << "data = " << data << endl; // 100
// 3. 通過變數的形式初始化 const修飾的變數,系統會為其開闢空間
int a = 200;
const int b = a; // 系統直接為b開闢空間,不會把b放入到符號表中
p = (int*)&b;
*p = 3000;
cout << "*p = " << *p << endl; // 3000
cout << "b = " << b << endl; // 3000
// 4. const修飾自定義型別的變數,系統會分配空間
const Person per = { 100, "viktor" };
// p1.num = 1000; // error
cout << "num = " << per.num << ", name = " << per.name << endl; // 100, viktor
Person* p1 = (Person*)&per;
p1->num = 2000;
cout << "num = " << per.num << ", name = " << per.name << endl; // 2000, viktor
}
int main(int argc, char *argv[])
{
test();
return 0;
}
總結:
- C++中,使用
const
定義宣告變數時,該變數會先放入到符號表中,不會開闢記憶體空間; const
修飾的全域性變數預設是內部連結的;- 對
const
修飾的變數進行取地址操作,系統會對該變數開闢空間; - 使用其它的變數對
const
修飾的變數進行初始化時,系統會對該變數開闢空間; const
修飾自定義的資料型別,系統為自定義資料開闢空間。
3 C/C++中const異同總結
3.1 const修飾全域性變數
全域性變數儲存在只讀的文字常量區,所以C/C++中const
修飾的全域性變數都不可以更改變數對應的內容。
C中const
修飾的全域性變數預設是外部連結的,而C++中預設的是內部連結,如果想使得其變為外部連結可以在const
修飾的全域性變數前加extern
關鍵字。
3.2 const修飾區域性變數
const
在修飾基礎資料型別的區域性變數時,在C中會給該變數在棧中開闢記憶體空間,可以通過指標的方式修改變數的內容;而C++對於const
修飾基礎型別的變數是在符號表中新增一條記錄,不會在棧中開闢空間,所以不能通過指標的方式修改變數的值。
C++中對const
修飾的區域性變數是在如何使用的情況下才分配記憶體,如果對const
修飾基礎資料型別的區域性變數進行取地址操作,會對變數分配記憶體;使用一個變數初始化const
變數時會分配記憶體;const
修飾的自定義資料型別(結構體、物件)會分配記憶體。
4 const和#define
在舊版本C中,如果想建立一個常量,必須使用前處理器#define MAX 1024;
。使用巨集定義的MAX
對於編譯器來說不知其的存在,因為在程式預處理的過程中,所有的MAX
已經被替換為1024,於是MAX
並沒有將其加入到符號表中。
如果使用這個常量獲得一個編譯錯誤資訊時,可能會帶來一些困擾,因為這個資訊可能會提到1024,但是並沒有提到MAX
。
此外MAX
如果被定義在另外一個標頭檔案中,當前可能並不知道1024代表什麼,也許解決這個問題要花很長時間。解決辦法就是用一個常量替換上面的巨集。
const
和#define
的區別:
const
有型別,可進行編譯器型別安全檢查。#define
無型別,不能進行型別檢查
示例:
// 巨集沒有型別,const有
#define MAX 1024
const short my_max = 1024;
void func(short i)
{
cout << "short函式" << endl;
}
void func(int i)
{
cout << "int函式" << endl;
}
void test()
{
func(MAX); // int函式
func(my_max); // short函式
}
const
有作用域,而#define
不重視作用域,預設定義出到檔案結尾。如果定義在指定作用域下有效的常量,那麼#define
就不適用
示例:
// 巨集的作用域是當前的整個檔案,const的作用域以定義的情況決定
void my_func(void)
{
// 作用範圍是當前複合語句
const int my_num = 10;
// 作用範圍是當前位置到檔案結束
#define MY_NUM 10
}
void test()
{
// cout << "my_num = " << my_num << endl; // err 不識別
cout << "MY_NUM = " << MY_NUM << endl; // ok
}
const
可以作為名稱空間的成員,而如果將巨集作為名稱空間的成員,失去了名稱空間的意義,因為巨集作用於當前的檔案,而不是隻屬於某個名稱空間的
5 總結
const
由C++採用,並加進標準C中,儘管它們很不一樣,在C中,編譯器對待const
如同對待變數一樣,只不過帶有一個特殊的標記,意識是”你不能改變我“。在C++中定義const
時,編譯器為它建立空間,所以如果兩個不同檔案定義多個同名的const
,連結器將發生連結錯誤。簡而言之,const
在C++中用的更好。
在C++中儘量使用const
來替換巨集定義,避免不必要的麻煩。
另外在C++中可以使用變數定義陣列。對於C,在支援C99標準的編譯器中,可以使用變數定義陣列。
示例:
#include <iostream>
using namespace std;
void test()
{
int a = 10;
int arr[a];
int i = 0;
for(; i < 10; i++)
arr[i] = i;
i = 0;
for(; i < 10; i++)
cout << arr[i] << " ";
cout << endl;
}
int main(int argc, char *argv[])
{
test();
return 0;
}