常见的C/C++常量有:字面常量,符号常量,契约常量,枚举常量。
1、字面常量
字面常主要有基本数据常量、字符常量以及字符串常量,他们都会存储在程序的只读符号表中(非数据区)。
int age = 27 ; // 基本数据常量
char sex = 'm'; // 字符常量
char *name = "chen" ; // 字符串常量
// 如果试图修字符串常量( 仅能试图修改字符串常量,因为只能获取到字符串常量的地址,char *sex = &'m'; int *age = &27;都会报错 ):
printf( "试图修改指针:0x%p,指向内容\r\n",(name+0) );
*( name+0 ) = 'C';
// 编译不报错,运行时信息如下:
// 输出:修改指针:0x00417830, 指向内容
// 引用程序错误:"0x00414513"指令引用的"0x00417830"内存。该内存不能为"written"。
// 修改字符常量同理,如有兴趣可以尝试一下。
另外这里多讨论一种情况,如下例:
char name[]="chen";
printf( "原始内容:%s\r\n",name );
printf( "修改指针:0x%p, 指向内容\r\n",(name+0) );
*(name+0) = 'C';
printf( "修改之后的内容为:%s\r\n",name );
// 输出内容如下:
// 原始内容:chen
// 修改指针:0x0012FF48, 指向内容
// 修改之后的内容为:Chen
(请不要诧异这种情况,这里char name[]="chen";进行的操作是用字符串常量来初始化字符数组,所以并不是对字符串常量进行修改,只是对字符串数组进行修改,所以修改成功 )
小结:字面常量存储在只读符号表中,不能修改能够得到的常量地址所指向的值。
问题:什么是只读符号表?从内存地址来看,是接近静态数据区的,但不知道具体是什么东东。
2、符号常量
通过const或者define自定义的常量(宏)
const double EPSILON = 1e-6; // const 定义的常量
#define NULL 0 // define 定义的宏( 宏定义的常量,在预编译阶段就替换成对应的内容,所以本质为字面常量 )
这里分享一下对const的认识。
首先,C中表示不能修改的变量,因此会为此分配内存空间;
其次,C++中针对基本数据类型不分配存储空间(存储在符号表中),大数据或者extern的符号常量需要分配内存;
最后,const是编译时的一种手段,无法避免运行时的修改;
通过测试,C++在运行时对整型的处理和对浮点数的处理是有区别的,如下测试:
do{
#define out_put( out,tmp,typeStr,type,t ) do{ \
printf(""##typeStr":old=%"##t"\r\n",(type*)(out)); \
*(tmp)=10;\
printf(""##typeStr":new=%"##t"\r\n",(type*)(out));\
}while(0);
// 整型测试
const char c_const = 'c';
const bool b_const= false;
const int i_const = 1;
// char
char *tChar=(char*)&c_const;
out_put( c_const,tChar,"int",int,"c" );
// bool
bool *tBool=(bool*)&b_const;
out_put( b_const,tBool,"bool",bool,"d" );
// int
int *tInt = (int*)&i_const;
out_put( i_const,tInt,"int",int,"d" )
}while(0);
测试结果:
do{
int:old=c
int:new=c
bool:old=0
bool:new=0
int:old=1
int:new=1
}while(0);
结论:对于整型基础数据在运行时并没有得到改变,高质量C++中描述的是,基本数据常量是保存在符号表中,编译器会为他创建一个拷贝,通过其地址访问到的就是拷贝,而非原始符号常量。
然而对于浮点数的处理,如下测试:
do{
// 浮点型测试
const float f_const = 1.0f;
const double d_const = 1.0f;
float *tFloat=(float*)&f_const;
printf("old float:%.1f\r\n",f_const);
*tFloat = 10.1F;
printf("new float:%.1f\r\n",f_const);
double *tDouble=(double*)&d_const;
//out_put( &d_const,tDouble,"double",double,"f" );
printf("old Double:%.1f\r\n",d_const);
*tDouble = 10.1F;
printf("new Double:%.1f\r\n",d_const);
}while(0);
测试结果:
do{
old float:1.0
new float:10.1
old Double:1.0
new Double:10.1
}while(0);
结论:对于浮点数据在运行时得到了改变,浮点是有别于基本数据类型的,和自定义大数据类型都能在运行时进行修改。
3、契约常量
契约常量主要应用于带型参的函数声明,其实参本身没有使用const关键字,但被看作const对象。
void constTest( const int &num ){ ... }
int num = 300;
constTest( num );
4、枚举常量
enum COLOR{
RED = 0xFF0000,
GREEN = 0X00FF00,
BLUE = 0x0000FF,
}
printf("color:red 0x%x\r\n",RED );
printf("color:grn 0x%x\r\n",GREEN );
printf("color:blu 0x%x\r\n",BLUE );
最后,在谈谈类中的常量。
首先,非静态的常量是属于每一个对象的,在每个对象的生存期内,并且它的初始化只能在初始化列表中进行;
其次,在类中想建立常量,可以通过静态常量和枚举来实现。
例如:
class constClass
{
public:
const int num;
static const int id = 100;
enum _E_CONST{
BLUE = 0x0000FF,
GREEN = 0X00FF00,
RED = 0xFF0000,
};
constClass():num(200){
printf("color:red 0x%x\r\n",RED );
printf("color:grn 0x%x\r\n",GREEN );
printf("color:blu 0x%x\r\n",BLUE );
};
constClass &operator=( const constClass &cc ){};
~constClass(){};
};
测试:
constClass *cc = new constClass();
constClass *cc1 = new constClass();
printf("const 0x%p = %d\r\n",&cc->num,cc->num);
printf("const 0x%p = %d\r\n",&cc1->num,cc1->num);
printf("static const 0x%p = %d\r\n",&constClass::id,constClass::id );
printf("red = 0x%x\r\ngreen = 0x%x\r\nblue = 0x%x\r\n",constClass::RED,constClass::GREEN,constClass::BLUE );
delete cc;
delete cc1;
输出:
color:red 0xff0000
color:grn 0xff00
color:blu 0xff
color:red 0xff0000
color:grn 0xff00
color:blu 0xff
const 0x003965D8 = 200 // 非静态常量属于每一个对象
const 0x00394858 = 200 // 非静态常量属于每一个对象
static const 0x00416830 = 100 // 静态常量实现的恒定常量
red = 0xff0000 // 枚举实现的恒定常量
green = 0xff00 // 枚举实现的恒定常量
blue = 0xff // 枚举实现的恒定常量