C/C++常量的知识 20130918
语言的实现隐含着使用着一些常量,如初始化全局变量静态变量,另外还有一些我们不曾感觉到的变量:函数地址(也就是函数名称), 静态数组的名字,字符串常亮的地址。常量可以分为:字面常量,符号常量,契约性常量,bool常量,枚举常量
一、常量的种类
1.字面常量是保存在程序的符号表中而不是一般的数据区(实际上是静态存储区而不是栈中),这些符号是只读的,是一种访问保护机制。我们无法获取这些常量的地址。而且对于字符串常量,我们不可以使用字符串的地址修改他们的值
char * p= “yang”’; 相当于 const char *p = “yang”;
2.符号常量
使用#define定义的宏常量和使用const定义的常量。#define是预编译的指令,它定义的宏常量在进入编译阶段就已经被替换成所代表的字符串常量了,因此宏定义指令本质上是字面常量。在C语言中的const定义的常量是不可以修改的,因此会给他分配存储空间。但是在C++中的const定义的常量要分情况:对于基本数据类型的常量,编译器会把它放到符号表中二部分配存储空间,对于ADT/UDT的const常量则需要分配存储空间。
我们可以取一个const符号常量的地址:对于基本数据类型的const常量,编译器会重新在内存中创建他的一个拷贝,通过其地址访问到的就是这个拷贝而不是原始的符号常量;而对于构造类型的常量,实际上他是编译的时候不允许修改的变量,因此我们如果绕过编译器的静态类型安全检查机制,就可以在运行期间修改他的内容了。
const long lng = 10;
long *p1 = (long *) & lng;
*p1 = 10000;
cout << "pl " << p1 << endl;
cout << "&lng" << &lng << endl;
cout <<"*pl : "<< *p1 << endl;
cout << "lng:" << lng << endl;
class Integer{
public:
Integer():m_lng(100){}
long m_lng;
};
const Integer val;
Integer *pInt = (Integer*) &val;
(*pInt).m_lng = 999999;
cout << "*pInt: " << pInt->m_lng << endl;
cout << "val: " << val.m_lng << endl;
const只是在编译的时候(代码级语言层面)强类型的安全检查机制的一种手段,帮助编码人员发现无意中修改的代码,而在运行的时候无法阻止恶意的修改,也就是说的“防君子不防小人”。
从理论上说只要你掌握一个对象的指针(内存地址),我们就可以绕过编译器随意修改他的内容,除非该内存区域受到操作系统的保护。也就是说C++并没有提供对指针有效的静态检查,而是把它丢给了OS,这正是指针的危险所在。
C语言中const符号常量默认是外连接的(分配存储),所以我们不可以在两个编译单元中同时定义一个同名的const符号常量,或者是把一个const符号常量定义房子啊一个头文件中而在多个编译单元中同时包含该头文件。但是在C++标准中,默认是内连接的,因此可以定义在头文件中。当在不同的编译单元中包含该头文件,编译器会默认他们是不同的符号常量,因此每一个编译单元独立编译时会给他们分别分配存储单元,而在连接的时候进行常量合并。
3.契约常量
未使用关键字const但是被看做一个const对象的
void ReadValue(cons tint & num){ cout << num ;}
int main(){ int n = 999; ReadValue(n);}
4.枚举常量
C/C++的构造类型enum实际上是用来定义一些相关常量的集合。并且规定枚举常量的值是可以扩展的,并非受限一般的整数类型。
enum Gigntic{ SMALL = 10, MID = 100, BIG = 1000};底层如何实现根据C/C++提供山的不同实现而有所不同。
5.正确定义符号常量
#define MAX 100
const int MAX = 100;
const float PI = 3.1415926;
C++ 中的需要将对外公开的长濑那个放倒头文件中,不需要对外工卡的常量放在定义文件的头部,便于管理,将不同模块的常量放在一个公用的头文件中。
6.const #define
const和#define都可以用于定义常量,但是const有一下的优点:
const常量是由数据类型的,#define是没有数据类型的,编译器可以对前者进行静态类型的安全检查,后者只是字符串的替换工作,没有安全类型的检查,并且在字符串替换的时候肯呢过产生意料之外的错误(边际效应);
有些集成化调试工具可以对const常量进行调试,但是不可以对#define进行调试。
7.类中的常量
常量尽在类中有效,犹豫#define定义的宏常量是全局的,不能达到目的,所以在class 中山hi也难怪const修饰数据成员来实现。const确实存在,但是其含义不是我们所期望的。非静态const数据成员是属于每一个对象的成员,只是在某个对象的生存期限内是常量,而对于整个类来说他是可以变化的,除非是static const常量
在class中不能够初始化非静态的const成员,因为在来创建之前,编译器是无法知道size的值是什么
class A{ const ing SIZE = 100;// error ,企图在类声明中初始化const数据成员
int array[SIZE]; /error 位置size的value
}
非静态的const数据成员初始化只能够在构造函数的初始化列表中进行
Class A{
public:
const int SIZE;
A int (size): SIZE(size){}
}
对于整个class的常量,const就不起作用了,应该使用enum中的美剧常量来实现。枚举常量不会占用对象的存储空间,在编译的时候就会全部赋值而且是定义的一个匿名的枚举类型,但是不可以定义float
class A{
enum {SIZE1 = 100, SIZE2 = 1000};
int array1[SIZE1]; in array2[SIZE2];
}
另外一种方法就是使用static const
class MyClass{
public:
static const int SIZE = 100;
MyClass(){}
static int getSize(){
return SIZE;
}
};
MyClass val;
cout << val.SIZE << endl;
cout << MyClass::SIZE << endl;
8.实际应用中如何定义常量
C程序中的使用方法:
定义在标准的头文件中CommonDef.h
static cons tint MAX_LENGTH = 1024;
然后每一个使用它的编译单元 #include该头文件即可,或者在头文件中使用该宏定义。
在头文件中将符号常量声明为extern 如:
extern const int MAX_LENGTH;
并且在某个源文件中定义一次
const int MAX_LENGTH = 1024; 然后每一个使用它的编译单元#include上述的头文件即可。
对于enum类型,每一个使用它的编译单元#include该头文件即可。
对于C++程序:
1).在某个头文件中直接在某个名字空间中或者全局名字空间中定义符号常量并且初始化,有没有static无所谓。
//CommonDef.h
const int MAX_LENGTH = 1024;
每一个编译单元直接使用include该头文件。
2).在某个公用的头文件中并且在某个命名空间下,或者是全局名字空间中将常量声明为extern
//CommonDef.h
extern const int MAX_LENGTH ;
并且在对应的源文件中定义一次
const int MAX_LENGTH = 1024;
每一个编译单元include头文件即可
3).整形常量,在某个enum中定义,直接使用include该头文件
4).定义为某一个公用类 static const 数据成员并且初始化,或者是类内的枚举类型。
Utility.h
class Utility{
public:
static const int MAX_LENGTH;
enum{ TIME_OUT = 10};
}
//Utility.cpp
const int Utility::MAX_LENGTH = 1024;