前言
作为一个完全自学入门计算机编程领域并从事了4年相关工作的过来人的角度说明一下为什么不管你是打算学Java还是Python最好还是从C语言开始的原因。
原因千千万万但是最最重要的原因还是——C语言掌握好了你就理解了计算机数据和运算,以及与之相关的内存管理的本质。不了解这些,你自学也可以入门,也可以顺利的写出可运行的程序,但是总会有这样那样的问题等着你。
其中最最重要的问题就是程序在执行的时候涉及到的内存问题,如内存溢出。虽然有人会说很多高级语言中已经解决了内存溢出的问题,但是涉及到每一句函数的意思以及它们在运行时的状态的时候如果你熟悉C语言就可以很快的理解并建立相关的知识体系。
例如JVM的内存管理体系,JVM内存模型等。
Hello World
大多数人学习编程的第一次输出都会是这个,如果用Java语言设计这个输出程序是这样:
public class MyAll{ public static void main(String args[]){ System.out.printl("Hello World"); }}
其中的那句:
System.out.printl("Hello World");
就是向终端打印“Hello World”这个字符串。在这个程序的运行时状态中首先会由类加载器使用双亲委派模式从类路径中加载编译后的.class文件,然后会在Java虚拟机的方法区,堆和栈中加载相应的数据进去(由于没有创建对象,所以在堆中不会有对象),由于在main函数中又调用了系统的printl()方法,有了函数就会涉及到栈帧,因此又会在main函数的栈中压入新的栈帧;而有了栈帧就会接着出现函数的入栈出栈;同时如果涉及到了多线程,那么又会出现线程安全问题。。。。。
所以说,说一千道一万,学好C语言本能保证你会秒懂以上这些,但是至少可以让你有头绪处理问题,而不是两眼一抹黑。
换成C语言:
#include int main(){ puts("C语言中文网"); return 0;}
puts()函数在C语言中的功能也是向终端输出数据。在功能上与Java的printl()方法类似。但是Java作为一个面向对象的编程语言,它关注的是对象,即通过对象存储数据,并使用此对象所在类的方法处理这些数据。而C语言是面向过程的,那么C语言的函数在运行时又是一种什么情况呢?
慢慢来,不要迈太大,步子迈大了:
编译器
编译器是一种程序,它的作用有很多,但是最主要的作用还是编译。
那怎么理解编译?
首先,程序员们揪头发写出来的程序叫源程序,也称源代码。在源代码中程序员会调用各种库,例如在Java中的:
我们会用到各种别的平台的或者第三方的库,这些源代码中调用的库函数在运行之前是需要由编译器给编译的,编译的过程很复杂,主要的无非就是纠错,链接等等。经过编译器加工过的代码就成了可执行的代码了,我们平时所说的程序就是指经过编译后就可以直接运行的程序,这样的程序被称为可执行程序。在 Windows 系统下,可执行程序的后缀有 .exe 和 .com;在UNIX 系统(Linux、Mac OS 等)下,可执行程序没有特定的后缀,系统根据文件的头部信息来判断是否是可执行程序。
可执行程序的内部是经过编译后的一系列计算机指令和数据的集合,它们都是以二进制的形式存在的,CPU 可以直接识别;但是对于程序员来说,这些经过编译后的代码就会非常晦涩,难以记忆和使用。
例如,在屏幕上输出“VIP会员”,C语言的写法为:
puts("VIP");
编译后的二进制文件就成了:
是不是想死的心都有了?
而你(作为一个人类)倒是能一眼看懂“puts("VIP")”这几个字,但是悲剧的是CPU看不懂啊;而CPU可以看懂的二进制作为一个人类你又看不懂,这就太操蛋了。
于是,编译器横空出世了!
既然我们俩(人类和CPU)都不能彼此了解对方的语言,那么我干嘛不找个“中间商”做翻译?编译器就是能够识别代码中的词汇、句子以及各种特定的格式,并将他们转换成计算机能够识别的二进制形式的“胖翻译”,胖翻译给两位“太君”翻译的这个过程称为编译(Compile)。
编译也可以理解为“翻译”,类似于将中文翻译成英文、将英文翻译成象形文字,它是一个复杂的过程,大致包括词法分析、语法分析、语义分析、性能优化、生成可执行文件五个步骤,期间涉及到复杂的算法和硬件架构。对于学计算机或者软件的大学生,“编译原理”是一门专业课程,有兴趣的读者请自行阅读《编译原理》一书,这里我们不再展开讲解。