1、基本优化
(1)全局变量
全局变量绝不会位于寄存器中。使用指针或者函数调用,可以直接修改全局变量的值。因此,
编译器不能将全局变量的值缓存在寄存器中,但这在使用全局变量时便需要额外的(常常是不必要的)
读取和存储。所以,在重要的循环中我们不建议使用全局变量。
如果函数过多的使用全局变量,比较好的做法是拷贝全局变量的值到局部变量,
这样它才可以存放在寄存器。这种方法仅仅适用于全局变量不会被我们调用的任意函数使用。
(2)用switch()函数替代if…else…
(3)使用二分方式中断代码而不是让代码堆成一列
(4)带参数的宏定义效率比函数高。简单的运算可以用宏定义来完成。
(5)选择合适的算法和数据结构
选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,
那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,
而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,
执行效率更高。在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。
与数组索引相比,指针一般能使代码速度更快,占用空间更少。
(6)能使用指针操作的尽量使用指针操作,一般来说,指针比较灵活简洁,对于大部分的编译器,
使用指针生成的代码更短,执行效率更高。
(7)递归调用尽量换成内循环或者查表解决,因为频繁的函数调用也是很浪费资源的
查表是数据结构中的一个概念。查表的前提是先建表。
在C语言实现中,建表也就是将一系列的数据,或者有原始数据中提取出的特征值,
存储到一定的数据结构中,如数组或链表中。
查表的时候,就是对数组或链表查询的过程。常用的方式有如下几种:
a、对于有序数组,可以采用折半查找的方式快速查询。
b、 对于链表,可以根据链表的构建方式,进行针对性查询算法的编写。
c、大多数情况,可以通过遍历的方式进行查表。即从第一个元素开始,一直顺序查询到最后一个元素,逐一对比。
(8)使用增量或减量操作符
++x;原因是增量符语句比赋值语句更快。
(9)使用复合赋值表达式
x+=1;
能够生成高质量的程序代码
(10)代码中使用代码块可以及时回收不再使用的变量,提高性能。
变量的作用域从定义变量的那一行代码开始,一直到所在代码块结束。
(11)当一个函数被调用很多次,而且函数中某个变量值是不变的,
应该将此变量声明为static(只会分配一次内存),可以提高程序效率。
(12)循环:长循环在内,短循环在外。
2、用移位实现乘除法求余运算,避免不必要的整数除法
实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如:
a=a*9可以改为:
a=(a<<3)+a
采用运算量更小的表达式替换原来的表达式,下面是一个经典例子:
旧代码:
x= w % 8;
y= pow(x, 2.0);
z= y * 33;for (i = 0;i < MAX;i++)
{
h= 14 *i;
printf("%d", h);
}
新代码:
x= w & 7; /*位操作比求余运算快*/y= x * x; /*乘法比平方运算快*/z= (y << 5) + y; /*位移乘法比乘法快*/
for (i = h = 0; i < MAX; i++)
{
h+= 14; /*加法比乘法快*/printf("%d", h);
}
避免不必要的整数除法
整数除法是整数运算中最慢的,所以应该尽可能避免。一种可能减少整数除法的地方是连除,这里除法可以由乘法代替。
这个替换的副作用是有可能在算乘积时会溢出,所以只能在一定范围的除法中使用。
不好的代码:inti, j, k, m;
m= i / j /k;
推荐的代码:inti, j, k, m;
m= i / (j * k);
3、结构体成员的布局
(1)按数据类型的长度排序
把结构体的成员按照它们的类型长度排序,声明成员时把长的类型放在短的前面。
编译器要求把长型数据类型存放在偶数地址边界。在申明一个复杂的数据类型
(既有多字节数据又有单字节数据) 时,应该首先存放多字节数据,然后再存放单字节数据,
这样可以避免内存的空洞。编译器自动地把结构的实例对齐在内存的偶数边界。
(2)把结构体填充成最长类型长度的整倍数
把结构体填充成最长类型长度的整倍数。照这样,如果结