程序是一个指令序列,指令是对数据的处理。一定程度上,程序也可以理解为是变量和函数的组合(类与对象在本质上也是如此,因为其成员也是变量和函数方法)。
指令包含操作码和地址码,分别指向一个包含操作指令和数据的内存单元。
程序也是表达式的集合,表达式 = 操作数 + 操作符;
程序的整体和局部都可以通过输入、处理、输出三个部分去描述。
程序=数据结构(数据及关系表示、存储)+算法(数据处理步骤的描述)+程序语言(用程序语言去描述数据结构和算法)
程序是定义数据和对这些数据的操作(运算)
数据类型:值集合←→运算符;
数据结构:元素←→元素的增、删、改、查;
类:成员数据←→成员方法;
容器类:元素←→容器元素的增、删、改、查;
函数:函数局部变量←→函数局部变量的运算;
程序的赋值语句用等号=连接,在编程中的=号不同于数学上的等号。如编程中的赋值语句:i = i + 1,这在数学中是行不通的,在编程中表示一个赋值语句。编程中的赋值语句需要从右往左(右结合,与一般表达式的左结合不同)理解,也就是左边的值赋给右边的变量,用左边的值初始化或更新右边的变量。右边的表达式可以包含变量,但其值本身不会改变。如下面交换两个值的代码:
temp = a; //因为下一步想更新a的值,所以先把其值保存到temp a = b; b = temp;
函数是对一段代码的封装,让程序模块化,封装的时机可以在需要时进行。
函数的内容一般也可以分为输入、处理、输出三部分:形参和定义的局部变量形成输入、返回值形成输出。
函数声明、定义和调用时,其头部结构是相同的,提供的是一个接口。声明和定义时的形参相当于一个局部变量的声明,调用时需要对形参赋值,赋值后的形参就是实参。
对于库函数,我们需要熟悉的就是其接口,只需要知道其名字、能完成的功能,需要提供的实参。
调用时,如果用变量(已有值)给形参赋值,用于赋值的变量与被调用的函数内使用的变量属于不同的作用域,所以即使名字相同(因为作用域不同,才允许名字相同),其值也互不影响。
(如果想要修改用于赋值的变量,需要在函数中使用指针(C或C++)或引用(C++或Python的可变对象的引用。)
程序中有大量的命名工作,如对变量、常量、函数、类、对象命名,取的名字在程序中叫标识符。
命名的标识符我们追求尽量有意义,更优雅,但有时不要钻牛角尖,不要为找意义而找意义,它只是一个起标识作用的符号而已。代表一个内存单元(数据),或一个功能,用什么名字都行,只要在使用时保持一致。
数据结构是对于数据元素集合(或叫容器)而言的。因此在一个数据集中,不仅要考虑元素本身,还要考虑元素之间的关系(逻辑关系),还要考虑存得进去,取得出来(元素操作的访问),以及元素操作的增、删、改的操作,这些操作需要考虑效率(存储关系)。
不管是逻辑关系还是存储关系,都可以用额外的数据来存储(用数据来表示关系)。这时数据结构形成了两个集合,一个是元素本身的集合,另外一个就是关系的集合。
存储关系的链式存储(还有一种就是顺序存储)就是用额外的数据(C的结构体+指针,或Python的类+引用,必须用到复合数据类型)来表示元素的链式关系。
数据结构的逻辑关系有线性关系、树型层次关系、图形网状关系三种。
线性关系不管是顺序存储还是链式存储,其前后关系都可以直接表现出来,不需要额外的数据来表示。
对于树型层次关系,最简单的二叉树可以用顺序存储,此外的不规则的树可以用额外的数据来表示双亲、孩子、兄弟的位置。
对于图型网状关系,可以用额外的数据来存储某一顶点的边。如邻接矩阵、邻接表都是顶点和边的数据集合。
同样的数组(C或C++)或列表(Python),因为数组或列表不同的操作(定义不同函数),可以构造出不同的数据结构(如栈、队列)。
对于结构化编程的C语言程序,代码阅读需从main()开始,此函数是程序的干流,main()对其它函数的调用可以理解为支流。
程序的组织也是如此,可以在main()中搭建一个框架,分解为不同的函数在main()中调用。(main()就局部变量的作用域而方,与其它函数处理同等的地位)
Python中虽然没有强制要求声明main()函数,但出于清晰组织代码的需要,也可以由main()来调用其它函数,以形成清晰的主次调用关系。
在对话框程序中,一般也会有主类,由主类包含其它类的头文件,形成调用关系。
结构体和类的语法都是由程序语言提供给程序员的一种自定义复合(结构)数据类型的机制。
结构体和类定义完成后,便可如同声明变量变量一样声明结构体变量或类实例了。
一般使用英文句点.来引用结构体变量或类实例成员(成员数据或方法),如下面的语法:
结构体变量或类实例.结构体变量或类实例成员
把前面的部分理解为修饰前缀就好了。
strObj.var
你可以理解为:属于strObj的变量var;
obj.method()
你可以理解为:属于obj的方法method();
相当于是整体与部分的关系。
结构体和类其本质是一种模块化的封装。
一般的编程都不是完全从0开始的,一般的编程语言基于不同的操作系统提供不同的编译器或解释器。同时,高级编程语言都会提供函数库或类库。你不需要了解其是如何实现的,只需要了解其接口,知道怎样调用即可。同样的,你也可以积累自己的函数库或类库。
(Python中不叫库,叫模板,用import引入,C或C++中用include包含进来。)
程序处理的数据在程序中用变量、常量或数据容器来表示,指向一个内存单元。内存单元其实质是用序列号来表示的,但在高级语言中抽象为变量或常量。
变量或常量按所存储数据的大小由操作系统分配一块内存空间。
计算机系统是一个数据输入、处理、输出的系统(还包括数据存储),硬件如此,程序也是如此。
硬件内的模块、集成电路是如此,程序的整体、及作为部件的函数(或对象方法)也是如此。
对于控制台程序,输入输出是通过函数或关键字进行,如C的input()、printf(),C++的cin、cout,Python的input()、print()。
对于GUI(图形用户界面)程序,输入输出借助于窗体表单控件,变量与控件交互,控件与用户交互。
高级语言中有顺序、选择、循环三种控制结构。
选择、循环结构可以实现语句的跳转,改变语句的顺序流程。
在机器语言和汇编语言中是怎样实现的呢?是通过跳转指令和标签(表示内存地址)来完成的。(VB中的goto语句也是如此)
在硬件中是由控制器的程序计数器(PC寄存器)来实现跳转的(将值更新为要跳转到的内存地址)。
图形界面程序代码执行流程与控制台程序有所不同,後者是一种特殊的条件分支的事件驱动流程,根据winows的通知决定程序的下一步走向。
1 代码重用:如函数、模块、类的继承和多态(接口重用)。
2 考虑编写大型程序的需要。高级语言的语法要考虑编写大型程序及多人合作的需要,如模板这种语法的抽象。但初学者上手时都是从一些入门的小程序开始的,这里就有一个理解上的矛盾。
3 效率的考量:包括程序本身运行在时间和空间上的效率以及程序员编写程序的效率;
4 特定的应用领域与场景。如C更贴近底层,PHP更接近web领域。
很多复杂的软件都是从简单的版本开始的。所以一般一开始就想一步到位一个复杂、功能完备、规划良好的程序会很难。可以考虑增量的开发方式(逐步增加新的功能)、原型和补丁的方式(不断修正),也可是逐步函数化、模板化的方式,以及后续重新整合代码的方式。
(软件都会有版本的更新,既有bug的修正,也有功能的增减。)CPU是逻辑门(晶体管)、加法器、集成电路、模块的组合。世界上第一台电脑Eniac的编程是通过在接线板上插拔连接线的方式进行的,其实质就是改变模块的连接方式。
模块的不同组合可以形成不同的功能模块,这就是硬件的硬连接。
在一定程度上也可以理解为软件是改变模块的硬连接,实现软连接,从而实现不同的功能。
CPU的设计一定程度上是指令系统的设计,一款CPU(硬件功能)对应一套指令系统(软件功能)。指令系统中指令的不同组合便是不同的程序。
随着硬件的不断发展,硬件的集成度越来越高。一定程度上硬连接与软连接都可以实现相同的功能,所以有时会说硬件和软件的界限越来越模糊。
计算机计算如何才能实现自动化?冯诺依曼提出了“存储程序”的概念:
1 编写包含数据的指令序列(程序);
2 程序输入(键盘输入或由辅助存储器输入)到内存中存储;
3 由控制器逐条从内存中取出指令,译码并产生控制信号,由运算器执行运算;如此循环,直到程序结束,产生输出;
编程是是在编码的基础上进行的。
编程需要考虑数据表示(描述)的问题,数据存储的问题,还要考虑数据如何处理的问题。
字符编码是一个数据表示的问题,是一个字符数据化的问题,用二进制序列来表示特定的字符,如ASCII、Unicode。(同样的,图像的点、声音的振动幅度与频率也可以数字化。)
为什么电子计算机可以识别符合机器指令集的机器语言(二进制序列)呢?
电子计算机是能进行二进制布尔运算的开关电路(晶体管)的组合,所以事物抽象为二进制数据,并用二进制数据来表示指令,所以电子计算机自然可以识别。
CPU的设计一定程度上是指令系统的设计,一款CPU(硬件功能)对应一套指令系统(软件功能)。所以特定的CPU只能执行特定的与指令系统描述的二进制序列。
高级语言是对机器语言在操作系统层面的抽象,符合高级语言规则的程序经由对应的编译器或解释器翻译或解释为机器语言,计算机就能执行。
(高级语言除了提供语法规则、编译器或解释器以外,还会提供函数库或类库(Python中是模块)。)