2012.4.16总结(二)

来源:互联网 发布:剑网三道长捏脸数据库 编辑:程序博客网 时间:2024/05/16 11:11

1 表达式的求值顺序问题:

函数参数的评估求值顺序


分析下面代码片段的输出结果:
int i = 2010;
printf("The results are: %d %d", i, i+=1 );
函数参数的评估求值并没有固定的顺序,所以,printf() 函数的输出结果可能是2010、
2011,也可能是2011、2011 。
类似的还有:
printf("The results are: %d %d", p(), q() );
p() 和q() 到底谁先被调用,这是一个只有编译器才知道的问题。
为了避免这一问题的发生,有经验的工程师会保证凡是在参数表中出现过一次以上的变
量,在传递时不改变其值。即使如此也并非万无一失,如果不是足够小心,错误的引用同样
会使努力前功尽弃,如下所示:
int para = 10;
int &rPara = para;
int f(int, int);
int result = f(para, rPara *= 2);
推荐的形式应该是:
int i = 2010;
printf("The results are: %d %d", i, i+1 );
int para = p();
printf("The results are: %d %d", para, q() );

int para = 10;
int f(int, int);
int result = f(para, para*2);


操作数的评估求值顺序


操作数的评估求值顺序也不固定,如下面的代码所示:
a = p() + q() * r();
三个函数p()、q() 和r() 可能以6 种顺序中的任何一种被评估求值。乘法运算符的高优
先级只能保证q() 和r() 的返回值首先相乘,然后再加到p() 的返回值上。所以,就算加上再
多的括号依旧不能解决问题。
幸运的是,使用显式的、手工指定的中间变量可以解决这一问题,从而保证固定的子表
达式评估求值顺序:
int para1 = p();
int para2 = q();
a = para1 + para2 * r();
这样,上述代码就为p()、q() 和r() 三个函数指定了唯一的计算顺序:p() → q() → r()。


另外,有一些运算符自诞生之日起便有了明确的操作数评估顺序,有着与众不同的可靠
性。
例如下面的表达式:
(a < b) && (c < d)
C/C++ 语言规定,a < b 首先被求值,如果a < b 成立,c < d 则紧接着被求值,以计算整
个表达式的值。但如果a 大于或等于b,则c < d 根本不会被求值。类似的还有||。这两个运
算符的短路算法特性可以让我们有机会以一种简约的、符合习惯用法的方式表达出很复杂的
条件逻辑。
三目条件运算符 ?: 也起到了把参数的评估求值次序固定下来的作用:
expr1 ? expr2 : expr3
第一个表达式会首先被评估求值,然后第二个和第三个表达式中的一个会被选中并评估

求值,被选中并评估求值的表达式所求得的结果就会作为整个条件表达式的值。
此外,在建议6 中将会详细介绍的逗号运算符也有固定的评估求值顺序。
请记住:
表达式计算顺序是一个很繁琐但是很有必要的话题:
针对操作符优先级,建议多写几个括号,把你的意图表达得更清晰。
注意函数参数和操作数的评估求值顺序问题,小心其陷阱,让你的表达式不要依赖计
算顺序。

2 逗号运算符

逗号分隔的表达式由于语言规则的不直观,容易产生理解上的误差。在使用逗号分
隔表达式时,C++ 会确保每个表达式都被执行,而整个表达式的值则是最右边表达式的
结果。

3 结构体中元素的布局

字节对齐

现代计算机中内存空间都是按照字节来划分的,从理论上来讲,对变量的访问可以从任
何地址开始;但在实际情况中,为了提升存取效率,各类型数据需要按照一定的规则在空间
上排列,这使得对某些特定类型的数据只能从某些特定地址开始存取,以空间换取时间,这就是字节对齐。

结构体默认的字节对齐一般满足三个准则:
(1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
(2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员自身大小的整数
倍,如有需要,编译器会在成员之间加上填充字节(Internal Adding)。
(3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在
最末一个成员之后加上填充字节(Trailing Padding)。

在编程应用中,如果空间紧张,需要考虑节约空间,那么就需要将结构体中的各个变量
按照上面的原则进行排列。基本的原则是:把结构体中的变量按照类型大小从小到大依次声
明,尽量减少中间的填充字节。

在某些时候,还可以通过编译器的pack 指令调整结构体的对齐方式。#pragma pack 的
基本用法为:
#pragma pack( n )
n 为字节对齐数,其取值为1、2、4、8、16,默认是8。
#pragma pack(1)// 设置1 字节对齐
struct A // 结构体A
{
int a;
char b;
short c;
};
将结构体A 的对齐方式设为1 字节对齐,那么A 就不再有填充字节了,sizeof(A) 的结
果即为各元素所占字节之和7。
请记住:
了解结构体中元素的对齐规则,合理地为结构体元素进行布局。这样不仅可以有效地节
约空间,还可以提高元素的存取效率。

原创粉丝点击