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];
char a, b;
float baz;
新代码,改进的顺序
double z[3];
double x, y;
long foo, bar;
float baz;
short ga, gu, gi;
(4)把频繁使用的指针型参数拷贝到本地变量
避免在函数中频繁使用指针型参数指向的值。因为编译器不知道指针之间是否存在冲突,所以指针型参数往往不能被编译器优化。这样数据不能被存放在寄存器中,而且明显地占用了存储器带宽。注意,很多编译器有"假设不冲突"优化开关(在VC里必须手动添加编译器命令行/Oa或/Ow),这允许编译器假设两个不同的指针总是有不同的内容,这样就不用把指针型参数保存到本地变量。否则,请在函数一开始把指针指向的数据保存到本地变量。如果需要的话,在函数结束前拷贝回去。
旧代码:
// 假设 q != r
void isqrt(unsigned long a, unsigned long*q, unsigned long* r)
{
*q = a;
if (a >0)
{
while (*q> (*r = a / *q))
{
*q = (*q+ *r) >> 1;
}
}
*r = a - *q* *q;
}
新代码:
// 假设 q != r
void isqrt(unsigned long a, unsigned long*q, unsigned long* r)
{
unsignedlong qq, rr;
qq = a;
if (a >0)
{
while (qq> (rr = a / qq))
{
qq = (qq+ rr) >> 1;
}
}
rr = a - qq* qq;
*q = qq;
*r = rr;
}
作者:chenlycly