给你n
个非负整数a1,a2,...,an
,每个数代表坐标中的一个点(i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为(i, ai)
和(i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n
的值至少为 2
。
关注本站官方公众号:程序员总部,领取三大福利! 福利一:python和前端辅导 福利二:进程序员交流微信群,专属于程序员的圈子 福利三:领取全套零基础视频教程(python,java,前端,php)
关注公众号回复python,免费领取,回复充值+你的账号,免费为您充值1000积分
所属网站分类: 技术文章 >
编译一个C++文件需要很长的时间,与C语言和Java相比。编译一个C++文件要比运行一个普通大小的Python脚本花费的时间要长得多。我目前使用的是VC++,但对于任何编译器都是一样的。为什么会这样?
我能想到的两个原因是加载头文件和运行预处理器,但这似乎不能解释为什么需要这么长时间。
每个编译单元都需要(1)加载甚至(2)编译成百上千的头文件。它们中的每一个通常都必须为每个编译单元重新编译,因为预处理器确保编译头的结果可能在每个编译单元之间有所不同。(宏可以在一个修改头内容的编译单元中定义)。
这可能是主要原因,因为它需要为每个编译单元编译大量的代码,此外,每个头都必须编译多次(每个包含它的编译单元一次)。
编译后,所有对象文件都必须链接在一起。这基本上是一个不能很好并行化的整体过程,必须处理整个项目。
语法分析极其复杂,严重依赖上下文,而且很难消除歧义。这需要很多时间。
在C_中,List是唯一编译的类型,不管程序中有多少个列表实例。在C++中,EDCOX1×1是完全独立的类型,与EDCOX1,2,每个都必须单独编译。
此外,模板构成了完整的图灵完整的"子语言",编译器必须解释它,这会变得非常复杂。即使相对简单的模板元编程代码也可以定义递归模板,从而创建几十个模板实例。模板还可能导致非常复杂的类型,具有可笑的长名称,从而给链接器添加了大量额外的工作。(它必须比较许多符号名,如果这些名称可以长成几千个字符,那就相当昂贵了)。
当然,它们加剧了头文件的问题,因为模板通常必须在头文件中定义,这意味着需要为每个编译单元解析和编译更多的代码。在纯C代码中,头通常只包含前向声明,但实际代码很少。在C++中,几乎所有的代码驻留在头文件中并不少见。
C++允许一些非常戏剧性的优化。C或Java不允许类被完全消除(它们必须在那里进行反射),但是,即使是一个简单的C++模板元程序也可以很容易地生成几十个或几百个类,所有这些都在优化阶段被内联并再次消除。
此外,编译器必须完全优化C++程序。C程序可以依赖于JIT编译器在加载时执行其他优化,C++没有得到这样的"第二次机会"。编译器生成的是尽可能优化的。
C++被编译成机器代码,它可能比字节码Java或.NET的使用更复杂一些(特别是在x86的情况下)。(这是出于完整性的考虑,只是因为在评论等中提到过。在实践中,这一步骤不太可能占用整个编译时间的一小部分以上)。
这些因素中的大部分是由C代码共享的,实际上C代码的编译效率相当高。解析步骤在C++中复杂得多,可以占用更多的时间,但是主要的违规者可能是模板。它们是有用的,并且使C++更强大的语言,但它们也会在编译速度方面付出代价。
对于任何编译器来说,这种减速并不一定是相同的。
两个主要的区别是一个非常强大的模块系统和一个允许单通道编译的语法。
编译速度当然不是C++编译器开发人员的首要任务,但是C/C++语法中也存在一些固有的复杂性,这使得处理过程变得更加困难。(我不是C的专家,但Walter Bright是,并且在建立各种商业C/C++编译器之后,他创建了D语言。他的其中一个改变是强制执行上下文无关的语法,以使语言更容易解析。)
另外,您会注意到,通常会设置makefiles,以便在C中分别编译每个文件,因此如果10个源文件都使用相同的include文件,那么include文件将被处理10次。
解析和代码生成实际上相当快。真正的问题是打开和关闭文件。记住,即使有include保护,编译器仍然打开.h文件并读取每一行(然后忽略它)。
有一次,一个朋友(在工作中感到无聊)拿起他的公司的应用程序,把所有的东西——所有的源文件和头文件——放进一个大文件中。编译时间从3小时下降到7分钟。
另一个原因是使用C预处理器查找声明。即使有了头罩,.h仍然需要反复分析,每次都包括在内。有些编译器支持预编译的头文件,这有助于实现这一点,但并不总是使用它们。
参见:C++经常质疑答案
C++被编译成机器代码。所以您有预处理器、编译器、优化器,最后还有汇编程序,所有这些都必须运行。
Java和C语言被编译成字节代码/IL,Java虚拟机/.NET框架在执行之前执行(或JIT编译成机器代码)。
python是一种解释语言,也被编译成字节代码。
我相信还有其他的原因,但一般来说,不必编译为本机语言可以节省时间。
1)无限头修复。已经提到了。缓解措施(如pragma once)通常只对每个编译单元有效,而不是对每个构建有效。
2)工具链通常被分为多个二进制文件(make、预处理器、编译器、汇编程序、archiver、impdef、linker和dlltool,在极端情况下),每个调用(编译器、汇编程序)或每对文件(archiver、linker和dlltool)都必须始终重新初始化和重新加载所有状态。