插入过早的讨论是万恶之源
也就是说,这是我为了避免不必要的效率而习惯的一些习惯,在某些情况下,还使我的代码更简单,更正确。
这不是讨论一般原则,而是要注意一些事情,以避免在代码中引入不必要的低效率。
这可能应该合并到上面冗长的讨论中。在内部循环重复计算的循环内部循环会变慢,这是非常普遍的常识。例如:
for (i = 0; i < strlen(str); i++) {
...
}
如果字符串确实很长,这将花费大量时间,因为在每次循环迭代时都会重新计算长度。请注意,GCC实际上会优化这种情况,因为它strlen()
被标记为纯函数。
在对一百万个32位整数进行排序时,冒泡排序是错误的方法。通常,排序可以在O(n * log n)的时间内完成(或者在基数排序的情况下更好),因此,除非您知道数据会很小,否则请寻找至少为O(n *登录n)。
同样,在处理数据库时,请注意索引。如果您SELECT * FROM people WHERE age = 20
没有人的索引,则需要O(n)顺序扫描,而不是更快的O(log n)索引扫描。
整数算术层次
使用C进行编程时,请记住,某些算术运算比其他算术运算更昂贵。对于整数,层次结构如下所示(首先是最便宜的):
当然,编译器会像通常优化的东西n / 2
,以n >> 1
自动,如果您定位的是主流电脑,但如果你的目标嵌入式设备,你可能无法得到这样的奢侈。
并且,% 2
并且& 1
具有不同的语义。除法和模数通常四舍五入为零,但是它是由实现定义的。很好,>>
并且&
总是四舍五入为负无穷大,(我认为)这更有意义。例如,在我的计算机上:
printf("%d\n", -1 % 2); // -1 (maybe)
printf("%d\n", -1 & 1); // 1
因此,请使用有意义的内容。别以为% 2
当初写作时会用自己做一个好男孩& 1
。
昂贵的浮点运算
避免重浮点运算像pow()
,并log()
在代码中并不真正需要他们,整数时尤其如此。以读取数字为例:
int parseInt(const char *str)
{
const char *p;
int digits;
int number;
int position;
// Count the number of digits
for (p = str; isdigit(*p); p++)
{}
digits = p - str;
// Sum the digits, multiplying them by their respective power of 10.
number = 0;
position = digits - 1;
for (p = str; isdigit(*p); p++, position--)
number += (*p - '0') * pow(10, position);
return number;
}
这种使用pow()
(以及使用它所需的int
<-> double
转换)不仅非常昂贵,而且还为精度损失创造了机会(顺便说一句,上面的代码没有精度问题)。这就是为什么我在非数学环境中看到这种类型的函数时会畏缩的原因。
此外,请注意,下面的“聪明”算法在每次迭代中都乘以10,实际上比上面的代码更简洁:
int parseInt(const char *str)
{
const char *p;
int number;
number = 0;
for (p = str; isdigit(*p); p++) {
number *= 10;
number += *p - '0';
}
return number;
}