y; /* 位移乘法比乘法快 */
for (i = h = 0; i < MAX; i++)
{
h += 14; /* 加法比乘法快 */
printf("%d",h);
}
(5)、避免不必要的整数除法
整数除法是整数运算中最慢的,所以应该尽可能避免。一种可能减少整数除法的地方是连除,这里除法可以由乘法代替。这个替换的副作用是有可能在算乘积时会溢出,所以只能在一定范围的除法中使用。
不好的代码:
int i, j, k, m;
m = i / j / k;
推荐的代码:
int i, j, k, m;
m = i / (j * k);
(6)、使用增量和减量操作符
在使用到加一和减一操作时尽量使用增量和减量操作符,因为增量符语句比赋值语句更快,原因在于对大多数CPU来说,对内存字的增、减量操作不必明显地使用取内存和写内存的指令,比如下面这条语句:
x=x+1;
模仿大多数微机汇编语言为例,产生的代码类似于:
move A,x ;把x从内存取出存入累加器A
add A,1 ;累加器A加1
store x ;把新值存回x
如果使用增量操作符,生成的代码如下:
incr x ;x加1
显
然,不用取指令和存指令,增、减量操作执行的速度加快,同时长度也缩短了。
(7)、使用复合赋值表达式
复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码。
(8)、提取公共的子表达式
在某些情况下,C++编译器不能从浮点表达式中提出公共的子表达式,因为这意味着相当于对表达式重新排序。需要特别指出的是,编译器在提取公共子表达式前不能按照代数的等价关系重新安排表达式。这时,程序员要手动地提出公共的子表达式(在VC.
NET里有一项“全局优化”选项可以完成此
工作,但效果就不得而知了)。
不好的代码:
float a, b, c, d, e, f;
。。。
e = b * c / d;
f = b / d * a;
推荐的代码:
float a, b, c, d, e, f;
。。。
const float t(b / d);
e = c * t;
f = a * t;
不好的代码:
float a, b, c, e, f;
。。。
e = a / c;
f = b / c;
推荐的代码:
float a, b, c, e, f;
。。。
const float t(1.0f / c);
e = a * t;
f = b * t;
4、结构体成员的布局
很多编译器有“使结构体字,双字或四字对齐”的选项。但是,还是需要改善结构体成员的对齐,有些编译器可能分配给结构体成员空间的顺序与他们声明的不同。但是,有些编译器并不提供这些功能,或者效果不好。所以,要在付出最少代价的情况下实现最好的结构体和结构体成员对齐,建议采取下列方法:
(1)按数据类型的长度排序
把结构体的成员按照它们的类型长度排序,声明成员时把长的类型放在短的前面。编译器要求把长型数据类型存放在偶数地址边界。在申明一个复杂的数据类型 (既有多字节数据又有单字节数据) 时,应该首先存放多字节数据,然后再存放单字节数据,这样可以避免内存的空洞。编译器自动地把结构的实例对齐在内存的偶数边界。
(2)把结构体填充成最长类型长度的整倍数
把结构体填充成最长类型长度的整倍数。照这样,如果结构体的第一个成员对齐了,所有整个结构体自然也就对齐了。下面的例子演示了如何对结构体成员进行重新排序:
不好的代码,普通顺序:
struct
{
char a[5];
long k;
double x;
} baz;
推荐的代码,新的顺序并手动填充了几个字节:
struct
{
double x;
long k;
char a[5];
char pad[7];
} baz;
这个规则同样适用于类的成员的布局。
(3)按数据类型的长度排序本地变量
当编译器分配给本地变量空间时,它们的顺序和它们在
源代码中声明的顺序一样,和上一条规则一样,应该把长的变量放在短的变量前面。如果第一个变量对齐了,其它
变量就会连续的存放,而且不用填充字节自然就会对齐。有些编译器在分配变量时不会自动改变变量顺序,有些编译器不能产生4字节对齐的栈,所以4字节可能不对齐。下面这个例子
演示了本地变量声明的重新排序:
不好的代码,普通顺序
short ga, gu, gi;
long foo, bar;
double x, y, z[3];