《一站式学习C编程》读书笔记

第一章 程序的基本概念

1.1 程序和编程语言

程序(Program)告诉计算机应如何完成一个计算任务。

程序由一系列指令(Instruction)组成,指令是指示计算机做某种运算的命令,通常包括以下几类:
输入(input)
从键盘、文件或者其它设备获取数据。
输出(output)
把数据显示到屏幕,或者存入一个文件,或者发送到其它设备。
基本运算
执行最基本的数学运算(加减乘除)和数据存取。
测试和分支
测试某个条件,然后根据不同的测试结果执行不同的后续指令。
循环
重复执行一系列操作。
编写程序可以说就是这样一个过程:把复杂的任务分解成子任务,把子任务再分解成更简单的任务,层层分解,直到最后简单得可以用以上指令来完成。

表 1.1 一个语句的三种表示

编程语言 表示形式
C语言 a = b+1;
汇编语言 mov 0x804a01c,%eax
add $0x1, %eax
mov %eax, 0x804a018
机器语言 a1 1c a0 04 08
83 c0 01
a3 18 a0 04 08

计算机只能对数字做运算,符号、声音、图像在计算机内部都要用数字表示,指令也不例外,上表中的机器语言完全由十六进制数字组成。最早的程序员都是直接用机器语言编程,但是很麻烦,需要查大量的表格来确定每个数字表示什么意思,编写出来的程序很不直观,而且容易出错,于是有了汇编语言,把机器语言中一组一组的数字用助记符(Mnemonic)表示,直接用这些助记符写出汇编程序,然后让汇编器(Assembler)去查表把助记符替换成数字,也就把汇编语言翻译成了机器语言。

从上面的例子可以看出,汇编语言和机器语言的指令是一一对应的,汇编语言有三条指令,机器语言也有三条指令,汇编器就是做一个简单的替换工作

从上面的例子还可以看出,C语言的语句和低级语言的指令之间不是简单的一一对应关系,一条a=b+1;语句要翻译成三条汇编或机器指令,这个过程称为编译(Compile),由编译器(Compiler)来完成,显然编译器的功能比汇编器要复杂得多。用C语言编写的程序必须经过编译转成机器指令才能被计算机执行,编译需要花一些时间,这是用高级语言编程的一个缺点,然而更多的是优点。首先,用C语言编程更容易,写出来的代码更紧凑,可读性更强,出了错也更容易改正。其次,C语言是可移植的(Portable)或者称为平台无关的(Platform Independent)。

图 1.1. 编译执行的过程

编译执行的过程

图 1.2. 解释执行的过程

解释执行的过程

1.2 自然语言和形式语言

关键字:自然语言(Natural Language)、形式语言(Formal Language)、歧义性(Ambiguity)、冗余性(Redundancy)、隐喻(Metaphor)、字面(Literal)、语法(Grammer/Syntax)、符号(Token)、结构(Structure)、解析(Parse)、上下文(Context)、语义(Semantic)

自然语言(Natural Language)就是人类讲的语言。

这类语言不是人为设计(虽然有人试图强加一些规则)而是自然进化的。

形式语言(Formal Language)是为了特定应用而人为设计的语言。

形式语言有严格的语法(Syntax)规则。

语法规则是由符号(Token)和结构(Structure)的规则所组成的。

虽然形式语言和自然语言有很多共同之处,包括Token、结构和语义,但是也有很多不一样的地方。

歧义性(Ambiguity)

自然语言充满歧义,人们通过上下文的线索和自己的常识来解决这个问题。形式语言的设计要求是清晰的、毫无歧义的,这意味着每个语句都必须有确切的含义而不管上下文如何。

冗余性(Redundancy)

为了消除歧义减少误解,自然语言引入了相当多的冗余。结果是自然语言经常说得啰里啰嗦,而形式语言则更加紧凑,极少有冗余。

与字面意思的一致性

自然语言充斥着成语和隐喻(Metaphor),我在某种场合下说“The other shoe fell”,可能并不是说谁的鞋掉了。而形式语言中字面(Literal)意思基本上就是真实意思,也会有一些例外,例如下一章要讲的C语言转义序列,但即使有例外也会明确规定哪些字面意思不是真实意思,它们所表示的真实意思又是什么。

1.3 程序的调试

关键字:Bug(臭虫)、Debug(调试)、Run-time(运行时)

编程是一件复杂的工作,因为是人做的事情,所以难免经常出错。据说有这样一个典故:早期的计算机体积都很大,有一次一台计算机不能正常工作,工程师们找了半天原因最后发现是一只臭虫钻进计算机中造成的。从此以后,程序中的错误被叫做臭虫(Bug),而找到这些Bug并加以纠正的过程就叫做调试(Debug)。有时候调试是一件非常复杂的工作,要求程序员概念明确、逻辑清晰、性格沉稳,还需要一点运气。调试的技能我们在后续的学习中慢慢培养,但首先我们要区分清楚程序中的Bug分为哪几类。

  1. 编译时出错

    编译器只能翻译语法正确的程序,否则将导致编译失败,无法生成可执行文件。

  2. 运行时错误

    编译器检查不出这类错误,仍然可以生成可执行文件,但在运行时会出错而导致程序崩溃。(注意区分编译时和运行时(Run-time)这两个概念)

  3. 逻辑错误和语义错误

    编译和运行都顺利进行,但是程序没有干它该干的事情(程序员想要的目的)。

1.4 第一个程序

代码如下:

#include <stdio.h>

/*main: generate some simple output*/

int main()
{
	printf("Hello, World.\n");
	
	return 0;
}

Linux下编译(gcc):

gcc main.c
./a.out
Hello, World.

Windows下编译(MinGW):

gcc main.c
a.exe
Hello, World.

代码解释:

虽然这只是一个很小的程序,但我们目前暂时还不具备相关的知识来完全理解这个程序,比如程序的第一行,还有程序主体的int main(void){...return 0;}结构,这些部分我们暂时不详细解释,读者现在只需要把它们看成是每个程序按惯例必须要写的部分(Boilerplate)。但要注意main是一个特殊的名字,C程序总是从main里面的第一条语句开始执行的,在这个程序中是指printf这条语句。

/*...*/结构是一个注释(Comment),其中可以写一些描述性的话,解释这段程序在做是什么。编译器会忽视从/*到*/的所有字符,所以写注释没有语法规则,,并且不管写多少都不会被编译进可执行文件中。

printf的作用是把消息打印到屏幕。注意语句的末尾以" ; "号(Semicolon)结束,下一条语句" return 0; "也是如此。

C语言用{}括号(Brace或Curly Brace)把语法结构分成组,在上面的程序中printf和return语句套在main的{}括号中,表示它们属于main的定义之中。我们看到这两句相比main那一行都缩进(Indent)了一些,在代码中可以用若干个空格(Blank)和Tab字符来缩进,缩进不是必须的,但这样使我们更容易看出这两行是属于main的定义之中的,要写出漂亮的程序必须有整齐的缩进,第9.1节将介绍推荐的缩进写法。

猜你喜欢

转载自www.cnblogs.com/-Abandon-/p/13396891.html