C/C++中的const
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;
}