菜鸟笔记
提升您的技术认知

程序员修炼必读清单 ( 二)

阅读 : 1260

4. 编程语言

一个优秀程序员一定不是只掌握一门编程语言,每个语言都有它的优点,同时有它的缺点,有它适应的应用场景,无论是从语言本身还是从软件工程学角度来讲。可以这么说,一个优秀的程序员,可以不依赖于任何一门开发语言,并且可以选择多种开发语言实现复杂系统。之前写过一篇好的程序员应该熟悉的几门编程语言,虽然现在语言有所发展,趋势有所变化,但大部分仍然是适用的。今天我们重拾旧题,一名优秀程序员应该熟悉以下几种编程语言:

4.1 汇编语言

程序员是跟计算机打交道的,是要用指令控制计算机,而汇编语言是控制计算机最直接的编程语言,每一行代码都是一个独立的指令,程序员可以发挥最大的聪明才智,写出最高效的代码,这些对于一些与硬件打交道的程序员是必不可少的。无论什么样的操作系统,启动代码(Bootloader)都是由全部或部分的汇编语言写成的,甚至操作系统本身也有部分汇编语言代码。然而,对于一个软件项目而言,除了可行性、 性能,还要考虑一个重要的指标,就是成本,汇编语言的开发、维护成本是很高的,所以只能用在刀刃上。
2004年秋,一个朋友找到我,他在做一个电子锁的项目,之前是用汇编语言写的,大约有2万行,一个月之内就要交付项目了,否则面临着的是违约,而原来的开发人员失联了。在我看了代码之后毅然决然地决定用C语言重写,终于一个月之内,用2500的C代码完成了所有的软件功能与硬件联调测试,顺利交付到了李亚鹏家,据说装锁当天还见到了王菲(为什么没叫上我?)C与汇编语言的开发、维护成本可见一斑。
但是,只有了解了汇编,才能知道其它高级编程语言,最后是如何被解释、编译、执行的,才能知道怎样写出高效的代码。
记得2005年在做一个单片机的项目时,由于片上的ROM有限,最后编译完的代码超过了ROM的容量,然后就通过不断改变C语言的表达语句,争取在完成同样功能下生成的汇编代码量比较少,do-while, while, -for各种循环都试一遍,再选择一个较好的方式。如果用汇编语言更容易控制一些,不需要依赖编译器的编译水平。
现在很少用汇编的,平时用不到没必要花太多精力,但通读一遍,理解一些指令,了解一下哪些指令执行速度快,哪些指令执行速度慢是必要的,这样你才能知道在用高级语言写算法的时候为什么用移位操作代替,比如:
a = b / 8
会写成
a = b >> 3,
b = a * 8
会写成
b = (a << 3) & 0x07
因为移位操作只需要一条指令即可执行完成,除法会被转成N个减法,而乘法会被转成N次加法来实现,效率不在同一个量级上,如果是图像处理,每一像素都要进行乘除运算的话与使用移位操作来对比,处理一张图片的时间可能直接影响到一个系统的可用性,比如动态的人脸识别。

4.2 C/C++

C/C++经常被放在一起,当成一种编程语言来看待,也对也不对。也为两种语言的编程思想就不太想同,C语言属于过程式编程语言,而C++则是支持面向对像编程,而两都又关系密切,所以常放在一起,但差别真的挺大,而且C++的学习难度要远大于C。关系密切就在于都有一个C,而差异在于2个加号++,为什么是两个加号而不是一个加号,就是因为差异太大,C++是C的一个超集,在支持面向对像编程的同时,完全兼容了C语言的特性。
要学习C语言的原因在于C语言是与操作系统交互最流行最普遍的语言,所有的操作系统提供的API都是基于C语言的,然后才会在此基础上封装成其它编程语言,所以,在一些计算密集型的应用上,C/C++被广泛使用,如果主要语言采用其它语言如Java或C#甚至Javascript、Python,对于核心的性能瓶颈处都可以通过C语言实现算法,几乎所有的其它高级编程语言都会提供与C语言的交互接口,记住,如果你在用其它的编程语言写一些比较消耗CPU的工作,如果实在解决不了性能问题,就可以考虑加一下用C/C++实现核心算法。
C/C++语言的最大优势在于对内存的控制灵活性,但最大的难度也在于对内存的管理上,一把双刃剑,练好了无敌,练不好自伤。
首先推荐的还是《C++ Primer》,此书是C++的经典教程,书中丰富的教学辅助内容、醒目的知识点提示,以及精心组织的编程示范,让这本书在 C++ 领域的权威地位更加不可动摇。无论是初学者入门,或是中、高级程序员提升,本书均为不容置疑的首选。

其次,推荐《Essentail C++》,该书以四个面向来表现C++的本质:procedural(面向过程的)、generic(泛型的)、object-based(基于对象的)、objectoriented(面向对象的)。全书围绕一系列逐渐繁复的程序问题,以及用以解决这些问题的语言特性来组织。循此方式,你将不只学到C++的功能和结构,也可学到它们的设计目的和基本原理。

《C++编程思想》,该书讲解深入浅出地讲解如休用C++去思考、解决问题,系统性、完整性很强。

然后建议有时间还是读一下C++之父[美] Bjarne Stroustrup的《The Design and Evolution of C++》,作者详细介绍了C++的发展史,C++语言设计的原委,可以更好了理解C++是如何被设计出来的,为什么要这么设计,怎么一步步演进的。

健壮性是代码质量的一个重要指标,如何能写出稳定的、健壮的C++代码,一定要学习下《Exceptional C++》,对异常安全的保障有详尽的讲解,什么时候要抛出异常,如何捕获异常,如何从异常中恢复,一定细读,即使不经常使用C++语言的程序员也应该认真阅读下本书。

效率又是代码质量的另一个重要指标,《Effective C++》、《More Effective C++》必读。一共给出了编写高效C++代码的90个准则,是高效代码编写的阶梯。


然后,模板,是C++中最难的一部分,如果能灵活使用模板编程,大型项目的可维护性可以显著提高,但难度相对也较大,C++标准库中的STL但是一些常用模板类的封装,它更是如神兵利器。

最后要推荐的就是C++模板编程的必读书《C++模板元编程》,对开源模板库boost中的MPL进行了比较详细的介绍。MPL是C++对函数式编程的一个精巧包装。

到了最后,其实还想推荐一本《深入浅出MFC》,它系统、深入介绍了微软Visual Studio中的MFC类库,其实是一个介绍图形化界面类实现的非常好的范例,该书对于图形化界面,无论是PC、Mac还是WEB甚至微信小程序,如果要从底层实现一个图形化界面的框架该书可以作为教科书。

4.3 C#/Java

C#和Java可以放在一起说,因为这两个语言很像,都是在操作系统上面又封装了一层运行时,相当于虚拟机,可以把C#和Java当成运行在虚拟机上的程序,这样的好处就是移植性,因为移植的时候只要有虚拟机即可,就是SDK和CLR,目前Java在所有的主流操作系统中可以运行,而C#也可以在Linux、Windows、MacOS甚至Android上运行。有了可移植性,跨平台软件的开发成本就会低很多,但也要牺牲一些特性,尤其是GUI程序,不同系统的差异还是有的,很难做到原生程序的性能和体验,商业软件嘛,多是要性能和成本综合考虑的,系统间的差异会随着时间越来越小,开发成本也会越来越低,再C#和Java的用武之地
有了虚拟机的隔离安全性和稳定性也会更好,C#、Java如果不去通过C/C++的接口调用一些操作系统的API很难把系统整死机,而C/C++相对就更容易一些。
Java和C#目前都是比较成熟的语言,尤其是Java有着无与伦比的生态优势力,对于大型的系统实现,依然是首选。我也在MacOS上进行测试了一下Java、C#、C++的函数拆分的已开通Split函数的性能,测试代码如下:
Java:

import java.util.Calendar;
import java.util.Date;

public class SplitPerformance {
  
    public static void main(String[] args)
    {
  
        String s = "字段1,字段2,字段3,字段4,字段5,字段6,字段7,字段8,字段9,字段10";

        Calendar cal = Calendar.getInstance();
        Date d0 = cal.getTime();
        String[] fields = null;
        for(int i=0 ;i<100000000; ++i){
  
            fields = s.split(",");
        }
        Date d1 = Calendar.getInstance().getTime();

        System.out.format("Time elapsed: %d s\n", d1.getTime() - d0.getTime());
    }
}

C#:

using System;

namespace Split
{
  
    class Program
    {
  
        static void Main(string[] args)
        {
  
            DateTime t0 = DateTime.Now;

            String s = "字段1,字段2,字段3,字段4,字段5,字段6,字段7,字段8,字段9,字段10";

            String[] fields = null;
            for (int i = 0; i < 100000000; ++i)
            {
  
                fields = s.Split(",");
            }

            DateTime t1 = DateTime.Now;
            TimeSpan ts = t1 - t0;
            Console.Out.WriteLine("Time elapse {0}s", ts.TotalSeconds);
        }
    }
}

C/C++:

#include 
#include 
#include 

int main(int argc, const char * argv[]) {
  
    // insert code here...
    char const* line =  "字段1,字段2,字段3,字段4,字段5,字段6,字段7,字段8,字段9,字段10";
    
    time_t t0, t1;
    
    time(&t0);
    size_t len = strlen(line)+1;
    char* buff = (char*)malloc(len);

    for(size_t n=0; n<100000000; ++n){
  
        strcpy(buff, line);
        char* fields[10] = {
  buff};
        int i=1;
        for(char* p = buff; *p != 0; ++p){
  
            if( *p == ','){
  
                fields[i] = p+1;
                i++;
                *p = 0;
            }
        }
        
        //    for(int j=0; j

测试结果:

Java:    25.427s
C#:         30.623776s
C++:    21s

结果充分证明了Java和C#的性能优化得还是相当好的,很久没有对比了过了,这个结果是让我吃惊的,C#在Windows上的表现可能会更好。
所以,一般的项目使用Java、C#都是没问题的,即使是计算机密集型的代码,尤其是C#支持unfafe模式,可以使用C++的语法,为什么是#,就是4个+号,也就是C++++就是因为它是C++的一个超超集,只是语法并不完全兼容C++的语法而特性是基本兼容的,所以学习C#的话上面C++的读一下也是大有裨益的。

C#必读书

《CLR Via C#》对CLR和.NET Framework 4.0进行深入、全面的探讨,并结合实例介绍了如何利用它们进行设计、开发和调试。这本书在语言方面讲解全面、深入,是学习C#首选,可以对C#有一个全面的了解,以后可以作为案头手册,随时翻阅。

之后再读《C#高级编程》可以略过语言本身的介绍,阅读关于更多界面开发和应用技术,如WebForm、消息队列、COM+、AD等Windows系统的编程基本一览无余,也是必读。

然后就是进阶阅读,如何写出高效的C#代码,《Effective C#》

Java必读书

对于语言本身首选《Java核心技术》,一套两本,对Java的特性讲解条理清晰,深入、全面,读完了可以作为案头手册,随时翻阅。


另一本《Java编程思想》也是非常好的,豆瓣的评分更高,值得细读,尤其是其面向对像的思想介绍,初学者一定好好串讲,这本书软件工程思想融入对Java特性的讲解中。

与C++、C#一样,也有一本介绍高效编程的《Effective Java》

Java语言诞生之初是想“Write Once,Run anywhere",但是用Java写桌面软件真的不多,除非是跨平台需求比较强烈,确实有几个非常好的软件是用Java写的,比如MagicDraw、IntelliJ、Eclipse等,但主要还是做服务或WEB比较多。WEB开发核心框架Spring还是要好好学一下的。

学习的不是仅Spring框架怎么使用,更应该是它的设计思想,如何才是学好了,就是自己思考一下,如果让自己来重新实现Spring能不能实现。而对于容器的学习,下面这本就很不错:

4.4 Javascript

Javascript现在已经不是只用于WEB前端开发了,现在成了一个全栈语言,除了开发WEB前端界面,还可以使用Node.js开发后端的服务,还可以使用Taro开发微信小程序、Android、iOS、快应用等移动端应用,真可是一门语言可以干所有的事了。本人还是比较喜欢Javascript,因为喜欢C系的语法,它足够简洁、优雅,不是太喜欢Python的语法,忍受不了Python的__init__, __del__这样的函数名,当然也可能习惯了C系的代码,此外,Javascript还是比Python性能要好,可以干更多的事儿。Javascript还有个Typescript的变种,语法更加严谨,更加趋向于面向对象编程,更适合开发大型的项目。
《Javascript权威南》必读;《Javascript高级程序设计》评分不低,但这本书没有读过,正打算买下来再复习一下。


但这两本书稍微有点儿旧,都是基于EMAC5的,后面的新性书中没讲,再读一下《深入理解ES6》就完美了。

对于TypeScript也一本就够了《深入理解Typescript》

4.5 Python

Python用得不多,因为不太喜欢,可推荐的不多,《Python核心编程》应该也够了。最开始的时候看Python的代码缩进都是强制性的,这样确实在语法层面融入软件工程学思想非常好,但语言设计之始考虑不够严谨,导致后面3.X与2.X的不兼容,很多开源代码不能直接使用,浪费大量人力物力;又设计出个虚拟环境,用来解决包的版本不兼容、开发语言版本不兼容的问题,其实是把问题搞得更糟,实践中解决问题更复杂。总得来讲还是不错的语言,它的流行性证明了一切,主要得益于前几年所谓大数据、人工智能的暴发式增长,凭着它的简单性迅速占领了程序员的桌面,但它真的不适合做大的项目。

4.6 结语

现在的编程语言很多,根据不同领域、不同岗位的程序员需要的主要语言各有不同,但无论哪种,要想做到优秀,C/C++都是必须要熟悉的,即使不太熟练,Java/C#至少要熟练使用一种,Javascript/Python必须熟练使用其一,甚至两种都要熟练使用。比如前些天在做爬是的验证码识别的时候选择了Javascript,在使用opencv是就费了一些周折,好在对C++比较熟悉,搞定比较快,否则使用Python可能会更方便一些,开源的东西,用的人多了坑就没那么多了。
当然用得多的还有PHP、GO、Swift等数不胜数,按编译过程分主要有:

  • 汇编语言:直接通过计算机所支持的指令集编程;性能可控性比较好,开发难度大,成本高,可移植性差,不同架构的CPU的指令集不同,适合控制硬件的应用,一般用于底层寄存器修改,上层再用C封装;
  • 编译型语言(C/C++):直接编译成二进制机器码再执行;这类语言有些可移植性并不高,如Basic,主要依赖于编译器,而C/C++的可移植性是很高的,因为GCC可以支持几乎所有的操作系统,而商业的Unix系统厂家也都提供了自己的商业编译器,性能更好;
  • 解释型语言(Python、Ruby等):无需编译,一边解释一边执行;
  • 即时编译型语言:C#、Java都是编译型语言,但又与C/C++等不同,它编译后并不是直接是机器码,而是一种中间语言,这种中间语言执行时再由虚拟机编译成机器码执行,不知道怎么命名,借助即时编译的技术(JIT,Just-in-time),命合为”即时编译型语言“以区分直接编译成二进制的编译型语言。
    另外一角度,从编译思想上分可以分为:
  • 结构化编程语言(过程式编程):如C语言等;
  • 面向对象程序语言:如C++、Java等;
  • 函数式编程语言:如Lisp等;
  • 逻辑式编程语言:如Prolog等
    而函数式编程语言、逻辑式编程语言适用范围较小,所以,没有应用时有所了解即可,有兴趣的也可以研究了一下,笔者没有研究太多。
    有了这些编程语言也可以再读一下《编译原理》以系统地了解编程语言的编译过程,从而方便日后解决疑难问题。

    语言,只是工具,最基础的工具学好了,有了较强的表达能力,才能与计算机之间进行流畅的交流,计算机才会听话,高效、可靠地为我们服务。然后,正如上面所说,每一类编程语言至少要熟悉一种,这样会掌握更多的表达方式,更多的与计算机交互的模式,才能在系统设计与实现时选择最合适的编程语言。
    然而,要学习的还有很多… (待序)