程序设计学习笔记(一)

人畜走避 posted @ 2009年4月01日 00:38 in 编程 , 975 阅读

 一、程序的基本概念

程序和编程语言

程序(Program)是一个精确说明如何进行计算(数学上的计算,符号运算)的指令序列。

计算机是由数字电路组成的运算机器,只能对数字做运算。
程序由一些列指令(Instruction)组成,指令是指示计算机做某种运算的命令。
指令包括:
        输入(Input)
        输出(Output)
        基本运算(执行最基本的数学运算,加减乘除),和数据存取。
        测试和分支(Branch)
        循环(Loop)
编写程序理应是一件相当复杂的工作。
在不同的编程语言中,指令具有不同的形式。通常指令专指机器语言(Machine Language)或汇编语言(Assembly Language),等低级语言(Low-level Language)中的指令,而在高级语言(High-level Language)中通常称为语句(Expression)。
汇编语言和机器语言的指令是一一对应关系,由汇编器(Assembler)查表将助记符换成数字。高级语言和低级语言的指令之间不是一一对应关系。
高级语言到机器指令的过程,成为编译(Compile),由编译器(Compiler)来完成。
编译器的功能比汇编器要复杂的多。
高级语言编程更容易,可读性更强,也容易改正。
高级语言是可移植的(Portable),或者称为平台无关的(Platform Independent),平台可以指j计算机系统结构(Architecture),
也可以指操作系统(Operating System),也可以指两者的结合。
不同的计算机系统结构有不同的指令集(Instruction Set),直接用某种计算机的汇编或机器指令写出来的程序只能在这种计算机上执行。可以把高级语言程序编译成该计算机自己的(Native)机器指令。
源代码(Source Code),目标代码(Object Code),可执行代码(Executable)。
有些高级语言以解释(Interpret)的方式执行,运行解释器(Interpreter)执行该源代码,解释器是一行一行地编译源代码,边翻译边执行的。
编程语言仍在发展演化:
        机器语言,第一代语言(1GL,1st Generation Programming Language)
        汇编语言,第二代语言(2GL,2nd Generation Programming Language)
        高级语言,第三大语言(3GL,3rd Generation Programming Language)
目前已经有了4GL和5GL的概念。4GL以后的语言更多是在描述要做什么(Declarative),而不是描述具体一步一步怎么做(Imperative),具体一步一步怎么做完全交由编译器或解释器决定。
自然语言和形式语言
自然语言(Natural Language)就是人类讲的语言,不是人为设计的(虽然有人试图强加一些规则),而是自然进化的。
形式语言(Formal Language)是为了特定应用而人为设计的语言。
编程语言也是一种形式语言,是专门设计用来表达计算过程的形式语言。
形式语言有严格的语法(Syntax)规则。语法规则是由关于符号(Token),和结构(Structure)的规则所组成的。
Token的概念相当于自然语言的单词和标点。关于Token的规则称为词法(Lexical)规则。
语法规则的第二个范畴是结构,也就是Token的排列方式。关于语句结构的规则称为语法(Grammar)规则。
当阅读一个自然语言的句子或者一中形式语言的语句时,不仅要搞清楚每个词(或Token)是什么意思,而且必须搞清楚整个句子
的结构是什么样子的。分析句子结构的过程称为解析(Parse)。还要顾及上下文(Context)。这些都属于语义(Semantic)的范畴。
形式语言和自然语言有很多共同之处,包括Token,结构和语义,但是也有不同:
歧义性(Ambiguity)
自然语言充满歧义。形式语言的设计要求是清晰地,毫无歧义的,这意味着每一个语句必须有确切的含义而不管上下文如何。
冗余性(Redundancy)
自然语言引入了相当多的冗余(为了消除歧义减少误解)。而形式语言则紧凑,极少有冗余。
与字面意思的一致性
自然语言充斥着成语和隐喻(Metaphor)。而形式语言中字面(Literal)意思基本上就是真实意思,也有特殊情况(转义序列),但
也都会明确规定哪些字面意思不是真实意思,它们所表示的真实意思又是什么。
说自然语言长大的人,往往有一个适应形式语言的困难过程。某种意义上,形式语言和自然语言之间的不同正像诗歌和说明文的区别。
形式语言远比自然语言紧凑。结构很重要,识别Token,分解结构。
细节的影响,拼写错误和标点错误在这些自然语言中可以忽略的小毛病会把形式语言搞的面目全非。
程序的调试
编程是复杂的工作,因为是人做的事情,所以难免出错。
程序中的错误(Bug),找到Bug并加以纠正的过程叫做调试(Debug)。
Bug分类:
编译时错误。语法错误是最简单最低级的错误。
运行时错误。运行时会出错而导致程序崩溃。
逻辑错误和语义错误。程序没有干他该干的事情。
gcc的-o参数自己指定生成的文件名。
每个程序按照惯例必须要写的部分(Boilerplate)。
注释(Comment)。
语句末尾有;号(Semicolon)。
C语言用{}号(Brace或Curly Bracee)把语法结构分成组
缩进(Indent)。缩进不是必须的,可以写出漂亮的代码。
gcc的-wall选项,让gcc提示出所有的警告信息,不管是严重的还是不严重的。
常量、变量和表达式
星号(Asterisk)
代码风格(Coding Style)
注释不能嵌套(Nes)使用
// comment,两个斜线(Slash)。从c++借鉴的语法,在C99中被标准化。
C语言的发展历史大致上分为三个阶段:Old Style C,C89(最早的语言规范),和C99.
双引号(Double Quote),字符串字面值(String literal)。双引号是字符串字面值的界定符(Delimiter)。
转义序列有两个作用:把普通字符转义成特殊字符,把特殊字符转义成普通字符。
\n和\r分别表示Line Feed和Carriage Return
常量
常量(Constant)是程序中最基本的元素,有字符常量(Character Constant)、数字常量和枚举常量。
计算机中整数和小数的内部表示方式不同,在C语言中是两种不同的类型,浮点数(Floating Point)。
printf中的格式化字符串(Format String),规定了后面几个数据以何种格式插入到这个字符中,%号(Percent Sign)后面加个字母c,d,f
在pringf中分别解释成字符型,整形和浮点型的转换说明(Conversion Specification),分别用后面的三个常量来替换它们,它们只是在格式化字符串
中占个位置,并不出现在最终的打印结果中,这种用法通常叫做占位符(Placeholder)。这也是一种字面意思与真实意思不同的情况,但是和转义序列又有区别:
转义序列是编译器在处理字符串字面值时转义的,而占位符是由printf解释的。
变量
变量(Variable)是编程语言中最重要的概念之一,变量是计算机存储器中的一块命名的空间,可以在里面存储一个值(Value),存储的值是可以随时改变的。
常量有不同的类型,变量也有不同的类型,变量的类型也决定了它所占据的存储空间的大小。
应该给变量起有意义的名字。变量的命名限制:必须以字母或下划线(Underscore)开头,后面可以跟若干个字母、数字,下划线,但不能有其他字符。
这个规则不仅适用于变量名,也适用于所有可以由程序员起名字的语法元素,如:函数名,宏定义,结构体成员名,这些统称为标识符(Identifier)
规定了一些单词不允许用作标识符,这些单词称为关键字(Keyword)或保留字(Reserved Word)。
一般来说应避免使用下划线开头的标识符,以下划线开头的标识符往往被编译器用作一些功能扩展,C语言库的实现也定义了很多以下划线开头的名字,很容易造成名字冲突。
最佳实践(Best Practice)
定义(Definition)和声明(Declaration)之间的关系是:如果一个声明同时也要求分配存储空间,则称为定义。
赋值
定义了变量之后,要把值存入到它们的存储空间里,可以用赋值(Assignment)语句实现。
变量一定要先定义再使用。
定义一个变量,就是分配一块存储空间并给它命名,给一个变量赋值,就是把一个值存到这块存储空间中。变量的定义和赋值也可以一步完成,这称为变量的初始化(Initialization)。
表达式
运算符(Operator),操作数(Operand),由运算符和操作数所组成的算式称为表达式(Expression)。
运算符是有优先级的(Precedence),不希望按默认的优先级运算则要加括号(Parenthesis)
等号也是一种运算符。
同样优先级的运算符是从左到右计算还是从右到左计算,这称为运算符的结合性(Associativity)。
规则的组合(Composition)。C语言规定了一组语法规则,只要符合它的规则,就可以写出任意复杂的组合。
两个整数相除的结果仍为整数,并且总是舍去小数部分。
向下取整的运算称为Floor,与之相对的,向上取整的运算称为Ceiling。
无论操作符是正是负总是把小数部分截断(Truncate)。
隐式类型转换(Implicit Conversion)
char型本质上就是整数,只不过取值范围比int小,所以char型和int型统称为整数类型(Integer Type)或简称整型。
字符可以用ASCII码的转义序列表示。
函数(Function),参数(Argument),函数调用(Function Call)。
函数调用也是一中表达式,这个表达式由函数调用运算符(也就是括号)和两个操作符组成,左边的操作数称为Function Designtor,是函数类型的(Function Type)。
计算结果:函数的返回值(Return Value)。
函数可以有side effect。
改变计算机存储单元里的数据或者做输入输出操作,这些都算Side Effect。
函数声明,函数定义,函数原型(Prototype)。
形参(Parameter),实参(Argument)。
函数定义中的变量称为局部变量(Local Variable),由于形参相当于函数定义中的变量,所以形参也相当于局部变量。
全局变量定义的时候不初始化,则初始值是0,如果局部变量在定义的时候不初始化,则初始值是不确定的。
语句块也构成了一个作用域,
把语句封装成函数,把变量变成函数的参数。
有些代码路径在任何条件下都执行不到,这称为Dead Code。
如果定义一个概念需要用到这个概念本身,则称它的定义是递归的(Recursive)。
数学上很多概念使用它自己来定义的。需要定义一个最关键的基础条件(Base Case)。
函数调用自己,自己直接调用或间接调用自己的函数称为递归函数。
写递归函数时一定记得写Base Case。
用循环能做的事用递归都能做,递归和循环是等价的。有的语言只有递归而没有循环。计算机硬件能做的所有事情就是数据存取,运算,测试和分支,循环(或递归)。
高级语言有丰富的语法特征,都是为了做这些事情提供方便。
 

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter