第六章、常量与变量

第六章、常量与变量

6.1 常量

常量是一种标识符,它的值在运行期间恒定不变。
C 语言用 #define 来定义常量(称为宏常量)。
C++ 语言除了 #define 外还可以用 const 来定义常量(称为 const 常量)。

【规则 6-1-1】 尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串。

例如: 
#define MAX 100 			/* C 语言的宏常量 */
const int MAX=100;			// C++语言的const常量
const float PI = 3.14159;	// C++ 语言的 const 常量

C++ 语言可以用 const 来定义常量,也可以用 #define 来定义常量。但是前者比后者有更多的优点:
(1) const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会 产生意料不到的错误(边际效应)。
(2) 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

【规则 6-1-2】在 C++ 程序中只使用 const 常量而不使用宏常量,即 const 常量完全取代宏常量。

【规则 6-1-3】如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。

例如:
const float RADIUS = 100;
const float DIAMETER = RADIUS * 2;

【规则 6-1-4】不能在类声明中初始化 const 数据成员,可用枚举实现

class A {...
	const int SIZE = 100; // 错误,企图在类声明中初始化 const 数据成员
	int array[SIZE]; // 错误,未知的 SIZE 
};

const 数据成员的初始化只能在类构造函数的初始化表中进行,例如

class A
{...
	A(int size); // 构造函数
	const int SIZE ; 
};

A::A(int size) : SIZE(size) // 构造函数的初始化表
{
... 
}
A a(100); // 对象 a 的SIZE值为100 
A b(200); // 对象 b 的SIZE值为200

怎样才能建立在整个类中都恒定的常量呢?别指望 const 数据成员了,应该用类中的枚举常量来实现。例如

class A 
{	
	...
	enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量 int array1[SIZE1];
	int array2[SIZE2];
};

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。
枚举常量的缺点是: 它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(如 PI=3.14159)

【规则 6-1-5】多线程环境下,使用全局变量必须避免访问冲突问题
说明:可以使用临界区、互斥量、信号量、事件或消息的方式解决冲突。


6.2 变量

原则 6.2.1 一个变量只有一个功能,不能把一个变量用作多种用途。
说明:一个变量只用来表示一个特定功能,不能把一个变量作多种用途,即同一变量取值不同时,其代表的意义也不同。

反例:
WORD DelRelTimeQue(void)
{
	WORD Locate;
	Locate = 3;
	/* Locate具有两种功能:位置和函数DeleteFromQue的返回值*/
	Locate = DeleteFromQue(Locate);
	return Locate; 
}

正例:
//正确做法:使用两个变量
WORD DelRelTimeQue (voi d)
{
	WORD Ret; 
	WORD Locate;
	Locate = 3;
	Ret = DeleteFromQue(Locate);
	return Ret; 
}

原则6.2.2 不用或者少用全局变量。
说明:全局变量是增大模块间耦合的原因之一,应尽可能的不用。必须要使用时,请参看以 下关于全局变量相关的条款。单个文件内部可以使用static的全局变量,可以将其理解为类的私有成员变量。

规则6.2.1 防止局部变量与全局变量同名。
说明:尽管局部变景和全局变量的作用域不同而不会发生语法错误,但容易使人误解。

规则6.2.2 严禁使用未经初始化的变量作为右值。
说明:特别是在 C/C++中引用未经赋值的指针,经常会引起系统崩溃。

建议6.2.1 构造仅有一个模块或函数可以修改、创建,而其余有关模块或函数只访问的全局变量,防止多个不同模块或函数都可以修改、创建同一全局变量的现象。
说明:明确过程操作变量的关系后,将有利于程序的进一步优化、单元测试、系统联调以及代码维护等。这种关系的说明可在注释或文档中描述。

正例:
代码中对全局变量 g_Param 和 g_Job 
注释如下: 
//---------------------------------------------------------
//				Init()		Input()		Print()		Process()
// g_Param		Create		Modify		Access		Acess
// g_Job 		Create		Modify		Access		Acess, Modify
//--------------------------------------------------------- 

解释: 四个函数 Init()、Input()、Print()、Process()和 g_Param、g_Job 两个全局变量,其中函数 Input()、Process()都可修改 g_Job,故此变量将引起函数间较大的耦合,并可能增加代码测试、维护的难度。

建议6.2.2 使用面向接口编程思想,通过API访问数据:
如果本模块的数据需要对外部模块开放,应提供接口函数来设置、获取,同时注意全局数据的访问互斥。
说明:避免直接暴露内部数裾给外部模型使用,是防止模块间耦合最简单有效的方法。

建议6.2.3 在首次使用前初始化变量,防止未经初始化的变量被引用,初始化的地方离使用的地方越近越好。
说明:尽可能在定义变量的同时初始化该变量(就近原则),如果变量的引用处和其定义处相隔比较远,变量的初始化很容易被忘记。如果引用了未被初始化的变量,可能会导致程序错误,本建议可以减少隐患。

反例1:
//不可取的初始化:无意义的初始化 
int speedup_factor = 0;
if (condition)
{
	speedup_factor = 2;
}
else
{
	speedup_factor = -1
}

反例2:
//不可取的初始化:初始化和声明分离 
int speedup_factor;
if (condition)
{
	speedup_factor = 2;
}
else
{
	speedup_factor = -1;
}

正例1:
//较好的初始化:使用默认有意义的初始化
int speedup_factor = -1; 
if (condition)
{
	speedup_factor = 2;
}

正例2:
//较好的初始化使用?:减少数据流和控制流的混合 
int speedup_factor = condition?2:-1;

正例3:
//较好的初始化:使用函数代替复杂的汁算流 
int speedup_factor = ComputeSpeedupFactor();

建议6.2.4 明确全局变量的初始化顾序,避免跨模块的初始化依赖。
说明:系统启动阶段,使用全局变景前,要考虑到该全局变量在什么时候初始化,使用全局变量和初始化全局变最,两者之间的时序关系,谁先谁后,一定要分析清楚,不然后果往往是低级而又灾难性的。

规则 6.2.3 移位操作一定要确定类型
说明:BYTE 的移位后还是 BYTE,如将 4 个字节拼成一个 long,则应先把字节转化成 long

unsigned char ucMove; 
unsigned long lMove; 
unsigned long lTemp;

正例:
ucMove = 0xA3;
lTemp = (unsigned long) ucMove; 
lMove = (lTemp << 8) | lTemp; /* 用 4 个字节拼成一个长字 */
lMove = (lMove << 16) | lMove;

反例:
unsigned char ucMove = 0xA3; 
unsigned long lMove;
lMove = (ucMove << 8) | ucMove; /* 用 4 个字节拼成一个长字 */
lMove = (lMove << 16) | lMove;
变量类型:
①.auto自动变量:
表明变量自动具有本地范围,在离开作用域,无论块作用域,文件作用域还是函数作用域,变量都会被程序隐藏或自动释放。然后等你重新进入该作用域,变量又重新被定义和调用。使用auto变量优势是无需考虑变量是否被释放。
②.static静态变量:
简单说就是在函数等调用结束后,该变量也不会被释放,保存的值还保留。即它的生存期是永久的,直到程序运行结束,系统才会释放,但也无需手动释放。
③.register寄存器型变量:
这个关键字要求编译器尽可能的将变量存储在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。但是这只是给系统的一个暗示,如果寄存器资源有限,系统也不会满足你的要求。register型变量存取速度比内存快很多,一般你在一些系统库文件,或诸如单片机官方接口库中使用较多,正常我们能用上较少。
④. extern外部变量:
它属于变量声明,extern int a和int a的区别就是,前者告诉编译器,有一个int类型的变量a定义在其他地方,如果有调用请去其他文件中查找定义。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值