这是一个同学问的问题,据说是一个面试问题。
for(inti =0; i < n; i ++)
和
for(inti =0; i < n; ++ i)
两个循环,在循环变量的更新上,一个是 i ++,一个是 ++ i。
性能有区别吗?
首先,我要说,我不是很赞同这类“谭浩强式的问题”。在我看来,对这类问题如数家珍,和编程能力一点儿关系都没有。
但是,对于一个对计算机感兴趣的孩纸,时不时地研究一下这类“犄角旮旯”的问题,还是一件很有意思的事情。
对于 i ++ 和 ++ i 在语法上的区别,相信大家都了解。
i ++ 是先取值,后 ++。
如果 i = 0,则 int a = i ++ 以后,a 为 0,i 为 1。
而 ++ i 是先 ++,后取值。
如果 i = 0,则 int a = ++ i 以后,a 为 1,i 为 1。
如果我们将 i ++ 和 ++ i 想成是两个函数,可以这样理解。
i ++ 要先暂存 i 的初值,然后对 i 进行加 1 操作,之后返回之前暂存的 i 的初值。伪码如下:
int j = i;
i= i +1;
return j;
而 ++ i,则不需要暂存 i 的初值,直接对 i 进行加 1 操作以后,返回新的 i 的值就好了。
i = i + 1;
return i;
这样看,相信大家就能一目了然了,i ++ 的过程由于需要暂存 i 的初值,所以,理论上,性能耗费会更高一些。
因此,如果大家看一些“上古”的程序设计书籍(以 C 语言为主),会提及,上面的 for 循环,用 ++ i 更好。
但是,在现代编程环境中,这一点性能偏差,完全可以忽略不计。
一方面,现代编译器的优化,使得编译器完全可以分析出,在上述 for 循环的 i ++ 以后,对 i 的结果并没有进行赋值使用。从而,编译器会进行优化,最后编译出的逻辑,不进行上面所说的无用的暂存。
另一方面,即使使用不优化的编译,有人统计过,对于现代计算机来说,循环次数要多达 10^47 次,才可以产生人类可以察觉的性能差异。
10^47 是什么概念?
整型可以存储的数字规模是 10^9 这个量级。同学们可以在自己的计算机上尝试一下,做一个循环 10^9 次的循环。每一次只是给一个整型赋值,看走完这个循环,需要多少时间?
以 C++ 为例,大概就是这样的一个程序:
对于大多数同学的计算机,相信这个循环需要的时间,是高于 1 秒钟的。在我的计算机上,需要大概 1.3 秒。
就算只需要 1 秒钟,10^47 是 10^9 的 10^38 倍。也就是,循环 10^47 次,大概需要 10^38 秒。
这是什么概念呢?同学们可以用计算机计算一下,10^38 秒大概是 3*10^30 这么多年。
因为一天不过 60 * 60 * 24 =86400 秒;
一年不过 86400 * 365 = 3.1536 * 10^7 秒。
我查了一下,太阳系大概诞生于 64 亿年前,即 6.4 * 10^9 年前。这么算,现代计算机循环 10^47 这么多操作的时间,亿亿个太阳系已经诞生了。
是亿亿,不是一亿。用 3*10^30 除以 6.4*10^9,得到的结果,比一亿个一亿,还要大五万倍左右。
所以,在通常使用的时候,i ++ 和 ++ i 的这个性能差距,如果在一些语言或者编译环境(解析环境)中真的存在,也完全可以忽略不计。
那么,在逻辑等价的情况下,应该使用 i ++ 还是 ++ i ?
在这种情况下,应该考虑的就是表意性。即哪种写法最好理解?
可惜,表意性是一个主观的事情,没有客观标准。因此,公说公有理,婆说婆有理。
或许这也就是为什么,在很多新兴语言中,干脆直接废除掉 i ++ 或者 ++ i 这种语法吧。
i = i + 1,多清晰:)
写完这篇小文,发现文中的计算,对于大家对数量级有一个直观的理解很有帮助。
很多同学曾经问我类似这样的问题:一个 O(n) 的算法,一秒钟就跑完了,换成 O(n^2) 的算法,怎么电脑没反应了?
答案是:电脑在反应,但需要相当长的时间。
假设你的 n 是 10 万左右。如果 O(n) 的算法用 1 秒钟,O(n^2) 的算法就要用大概 10 万秒钟。(不严格,因为 0.5n^2 也叫 O(n^2))
10 万秒钟,就是要一天多的时间。因为,上文计算了,一天不过 86400 秒,即 8 万多秒。所以,要真想看到 O(n^2) 算法的计算结果,耐心等着就好。
如果不想等这一天多的时间...... 那就是学习算法的意义了!
如果你想跟着小编一起学编程的话!
可以来我的C语言/C++编程学习交流俱乐部,【点击进入】!
还有(源码,零基础教程,项目实战教学视频),欢迎初学者和正在进阶中的小伙伴们!
网友评论