从零开始学C语言

戴晟晖 祝明慧等编著

前言

  • C语言具有很强的实用性,既可用来编写应用软件,也适合于编写系统软件。
  • http://www.rzchina.net

第1篇 C语言入门

  • 汇编语言又称符号语言,对机器指令进行简单的符号化,它也是利用计算机所有硬件特性并能直接控制硬件的语言。
  • 机器语言是第一代计算机语言
  • 使用自然语言描述算法是指用文字加上一些必要的数学符号来描述解决问题的算法。
  • 这种用二进制编写的程序也叫“目标程序”。
  • 用高级语言编写的程序称为“源程序”,源程序不能在计算机上直接运行,必须将其翻译成二进制程序后才能执行
  • 计算机解题算法分为两大类:数值运算算法和非数值运算算法
  • 在C语言程序中,我们可以通常按十进制写,如果写的是十六进制,需要以0x开头;八进制以0开头,如0123表示八进制的123,0x123,则表示十六进制的123。
  • 所谓面向对象,就是基于对象的概念,以对象为中心,类和继承为构造机制,认识、了解、刻画客观世界并开发出相应的软件系统。
  • N-S图也被称为盒图。

1.1 计算机语言的演变

  • 一般来讲,C程序开发过程要经历创建源程序、编译源程序、链接目标代码、运行可执行文件等过程,
  • C语言程序使用英文小写字母书写。大写字母一般用于符号常量或特殊用途。
  • 1.文件包含# include是文件包含,通用的格式是# include<文件名>或# include“文件名”,它属于预处理命令中的一种。文件包含的作用是将该程序编译时所需要的文件复制到本文件,再对合并后的文件进行编译。stdio.h是基本输入/输出的头文件,在上例中,我们用到输入/输出函数printf()
  • 链接过程是使用系统提供的“链接程序”进行的,链接后产生以“.exe”为扩展名的可执行目标程序
  • main()表示主函数,这是系统提供的特殊函数,每一个C语言程序有且只有一个main()函数。
  • 源程序的执行不是从开头进行的,而是从main()函数所在的位置开始的,func()函数是用户自定义函数。程序从main()函数开始执行,执行到main()函数中的第四行时,开始调用func()函数,程序转到func()函数,执行完func()函数中的所有语句后,再返回到主函数func()函数中继续执行。
  • C语言中,变量的定义必须符合标识符的命名规则,即标识符只能由字母(大小写均可)、数字和下画线3种字符组成,第1个字母不能是数字。
  • “/*”开头到“*/”结尾之间的内容表示注释,它可以在一行书写或分多行书写,可写在程序的任何位置。

1.1.1 机器语言

  • 符号常量是指用一个标识符代表一个常量。
  • 在C语言程序中,常量可以不经说明而直接引用,而变量则必须遵守“先定义,后使用”的原则
  • 标识符是指用来标识常量名、变量名、函数名、数组等对象,按照一定的命名规则定义的字符序列,即一个代号。
  • 数据类型关键字(12个):char、double、enum、float、int、long、short、signed、struct、union、unsigned、void。❑ 控制语句关键字(12个):break、case、continue、default、do、else、for、goto、if、return、switch、while。❑ 存储类型关键字(4个):auto、extern、register、static。❑ 其他关键字(4个):const、sizeof、typedef、volatile。
  • 初始化实际上是一个赋值语句。(2)在定义变量的时候,可以只给部分变量赋值。
  • 符号常量名习惯上用大写,以便与变量名相区分

1.1.2 汇编语言

  • 整型数据分为一般整型、短整型和长整型,并且每一种类型又分为带符号和无符号两种类型
  • 整型常量的数据类型是整数,包括正整数、负整数和零。
  • 字符型常量包括由一对单引号括起来的一个字符构成的一般字符常量和由反斜杠(\)开头的特定的字符序列构成的转义字符。
  • 字符型变量就是用一个标识符表示字符型数据,并且该标识符的值可以发生变化。字符变量只能存放一个字符。
  • 实数型变量分为单精度(float)、双精度(double)和长双精度三种类型。
  • 在C语言中,数据类型可分为基本数据类型、构造数据类型、指针类型、空类型
  • 字符型(char)占一个字节,整型数据中普通整型(int)占4个字节、短整型(short)占2个字节和长整型(long)占4个字节。单精度浮点型(float)占4个字节,双精度浮点型(double)占8个字节。
  • 整型变量是指其值为整型数据的变量。整型数据有三种,即整型(int)、短整型(short int)和长整型(long int)。为了方便书写,我们将short int和long int后面的int省略,分别用short和long来表示短整型和长整型。
  • 字符型常量是由一对单引号括起来的一个字符。
  • 整型变量分为整型变量、短整型变量、长整型变量。
  • 字符型变量ch在内存中存储的不是字符a,而是字符a对应的ASCII代码的值97,换算成二进制数为01100001,因此字符型变量ch对应的内存单元中存放的是二进制数01100001。
  • 整型数据分为带符号数和无符号数,定义时在前面加上signed为带符号变量,加上unsign为无符号变量,因此整型变量归纳起来共有6 种变量类型
  • 实数型常量的类型都是双精度浮点型。
  • 将双精度转换成单精度,数据处理方法是数据所占的字节由8字节变成4字节;截取前面7位有效数字,数字可能不准确,注意溢出
  • 字符型数据由字母、符号和不用于算术操作的数字组成,又称为非数值型数据。

1.1.3 高级语言

  • 用关系运算符将两个表达式连接起来构成的式子称为关系表达式。
  • 关系运算符是用来比较两个运算量大小的运算符,实际上就是一种“比较运算”,运算的结果只能是“1”或“0”。
  • 用算术运算符将运算对象即运算量或操作数连接起来,构成符合C语言语法规则的式子,称为算术表达式。
  • 赋值运算符的作用是把运算符右边的表达式的值赋给其左边的变量,结合性是从右向左
  • 关系运算是指值与值之间的关系,逻辑运算是指将真值和假值连接在一起的方式。
  • 算术运算符包括基本算术运算符和自增、自减运算符
  • 在赋值表达式后添加一个分号,就成为赋值语句
  • 赋值运算符的左边必须为变量,而赋值表达式的左边可以是变量,也可以是赋值表达式。当赋值表达式的左边是赋值表达式的时候,应该带上括号。
  • 赋值运算符“=”左边必须是变量,右边可以常量、变量,也可以是函数调用或表达式。
  • 赋值表达式右边的表达式可以是一个算术表达式、关系表达式、逻辑表达式等,也可以是一个赋值表达式
  • 从级别比较高的数据类型向级别比较低的数据类型转换时需要进行强制类型转换。
  • %求余运算符(或称求模运算),只适合于整型数据和字符型数据。求余运算的结果符号与被除数相同,其值等于两数相除后的余数。
  • ➢ ++a:a的值先增加1后,再参与其他运算。➢ a++:a的值先参与其他运算,再使a的值增加1。➢ --a:a的值先减小1后,再参与其他运算。➢ a--:a的值先参与其他运算,再使a的值减小1。
  • 在C语言中,赋值操作不仅出现在赋值语句中,而且可以以表达式形式出现在其他语句中
  • 但并不是所有的条件表达式与if语句都能替换,只有当if语句中内嵌的语句为赋值语句,并且两个分支都赋给同一个变量时,才能用条件运算符代替。
  • 条件表达式中,表达式1的类型可以与表达式2、表达式3的类型不同,表达式2与表达式3的类型也可以不同,此时表达式值的类型为两者中较高者的类型。

1.1.4 面向对象或面向问题的高级语言

  • 表达式后面加上一个分号就构成了一个表达式语句。
  • 顺序结构、分支结构、循环结构
  • putchar()函数为字符输出函数,它的作用是在显示器上输出一个字符
  • 需要注意的是break语句只能跳出一层循环,即从当前的循环层中跳出。break语句不能用于循环语句和switch结构语句之外的任何其他语句中。
  • 输入数据时不能规定精度
  • continue语句只是结束循环结构中的本次循环,而不是跳出整个循环过程。
  • 如果输入的数据多于scanf()函数所要求的个数,余下的数据可以被下一个scanf()函数接着使用。
  • 在scanf()函数中某格式字符读入数据时,遇以下情况时认为该数据结束:❑ 遇“数据分隔符”,如空格、回车、tab或指定字符。❑ 遇宽度结束,如“%3d”,只取3列后读取结束。❑ 遇非法输入。

1.2 数制、数制转换与存储

  • C语言程序在执行的过程中要将源程序解释或编译成目标程序
  • else总是与它前面离它最近的未配对的if语句配对,与程序的缩进无关。

1.2.1 数制

  • 执行表达式2,表达式2是结束for语句循环的条件表达式。在语句执行之后,才执行表达式3。

1.2.2 数制的转换

  • ❑ 整数部分:除以进制取余数,直到商为0,余数从下到上排列。❑ 小数部分:乘以进制取整数,得到的整数从上到下排列。

1.2.3 计算机中数据的存储

  • 在计算机中,数据有三种表示方法:原码、反码和补码。计算机用一个二进制的最高位存放所表示数值的符号,最高位为0表示正数,最高位为1表示负数。对于一个正数,原码是将该数转换成二进制,它的反码和补码与原码相同。对于一个负数,原码是将该数按照绝对值大小转换成的二进制数,最高位即符号位为1;它的反码是除符号位外将二进制数按位取反,所得的新二进制数称为原二进制数的反码;它的补码是将其二进制的反码加1。计算机中任何一个带有符号的二进制数都是以补码形式进行运算和存储的。
  • 引用数组所有的元素称为遍历,遍历数组是通过循环来改变下标进行的。
  • 冒泡发的基本思路:将相邻两个数比较,将小的调到前面。

1.3.2 算法的特点

  • 在C语言中,实际参数向形式参数的数据传递是“值传递”,单向传递,只由实际参数传递给形式参数,而不能由形式参数传递给实际参数

1.3.3 算法的表示方法

  • 一个算法可以用多种不同的方法来描述,有自然语言、伪代码、流程图、N-S图等,
  • 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
  • 在定义一个函数时,该函数不能包含另一个函数,即在一个函数定义中,其函数体中不能包含另一个函数的完整定义。即在C语言中不能嵌套定义。
  • 预处理功能主要有以下三种: ❑ 宏定义; ❑ 文件包含; ❑ 条件编译。
  • 伪代码是一种在算法开发过程中用来表达思想的非形式化的符号系统。
  • 局部静态变量是在程序编译过程中被赋值的,且只赋值一次,在程序运行时其初值已经确定,以后每次调用函数时不再赋值,而是保留上一次函数调用结束时的值。
  • 函数“定义”是指对函数功能的确定,包括函数类型、函数名、参数及其类型、函数体等,它是一个完整的、独立的函数单位
  • 如果在程序中出现用双引号括起来的字符串内包含有与宏名相同的名字,预编译时并不进行宏替换。
  • 一个C程序可能由一个或多个源程序文件构成。如果程序由多个源程序文件组成,在一个文件中引用另一个文件中定义的全局变量,需要用extern关键字对全局变量做外部全局变量声明。

1.3.4 算法分析

  • 在计算机中,内存是以字节为单位的连续存储空间,每一个字节都有一个编号,这个编号称为地址。
  • 算法分析是对一个算法需要多少计算时间和存储空间做定量的分析
  • &和*运算符的优先级同++、--、!等运算符优先级别相同。并且是自右向左的结合方式。
  • 函数间传递数据有4 种形式:值传递方式、地址传递方式、返回值方式及外部变量传递。前两种方式需要利用函数参数传递数据,后两种方式不必使用函数参数来传递数据。
  • 算法的空间复杂度是指算法需要消耗的空间资源。
  • 指针变量的值是地址,是无符号的整型。但是,不能把整型常量赋给指针变量。
  • 对函数指针变量,像++p、--p、p--等运算是无意义的。
  • 程序中第6行“int (*p)[4]”表示p指向包含4个整型数据的一维数组。注意“(*p)”中括号是不能少的,int *p[4]表示定义一个指针数组。

1.4 C语言的发展简史和特点

  • 其中,“.”为成员运算符,其运算级别是最高的,和圆括号运算符“( )”、下标运算符“[ ]”是同一级别的,运算顺序是自左向右。
  • 结构体本身只是用户定义的一个数据类型,不占用什么存储空间,定义结构体变量以后,系统为其分配一定的存储空间,它在内存中是一个连续的内存单元,总字节数等于该结构型的所有成员所占用的字节数之和。

1.4.2 C语言的特点

  • C语言一共只有32个保留字,9种控制语句
  • C语言的数据类型有整型、实数型、字符型、数组类型、指针类型、结构体类型、联合体类型及枚举类型等

2.1 C语言程序的结构特征

  • 1.文件包含# include是文件包含,通用的格式是# include<文件名>或# include“文件名”,它属于预处理命令中的一种。文件包含的作用是将该程序编译时所需要的文件复制到本文件,再对合并后的文件进行编译。stdio.h是基本输入/输出的头文件,在上例中,我们用到输入/输出函数printf()、scanf(),因此需要在源程序的开头写上#

3.1.1 标识的命名

  • 标识符由字母(包括大写字母和小写字母)、数字及下画线组成,且第一个字符必须是字母或者下画线。

3.2 常量

  • 常量分为直接常量和符号常量。

3.3 变量

  • 变量是指在程序运行过程中其值可以改变的量。
  • C语言设置了以下变量:不同数据类型的变量、全局变量、局部变量、静态变量(静态全局变量和静态局部变量)、寄存器变量、外部变量等。

3.4 变量的初始化

  • 初始化实际上是一个赋值语句。 (2)在定义变量的时候,可以只给部分变量赋值。

4.1.2 构造数据类型

  • 数组是由相同类型的数据组合而成的

4.1.3 指针数据类型

  • 其值用来表示某个量在内存储器中的地址。

4.1.4 空类型

  • 但是,也有一类函数,调用后并不需要向调用者返回函数值,这种函数可以定义为“空类型”,其类型说明符为void。

4.4 字符型数据

  • 字符参与运算相当于对字符的ASCII码值进行运算。

4.4.1 字符型常量

  • “\0”是空字符,它的ASCII码值为0,表示NULL,是字符串的结束标志。

4.4.2 字符型变量

  • 字符型变量用来存放字符型常量,但它存储的不是字符本身,而是该字符对应的ASCII代码的值。
  • 字符数据只占一个字节,把字符看成无符号数时,它只能存放0~255范围内的整数;若是有符号数,则为-128~127。
  • 字符型变量可以用字符形式输出(即%c),也可以用整数形式输出(即%d),但是应注意字符数据只占一个字节。

4.5.1 自动类型转换

  • 不同类型的数值进行运算时,系统会自动将级别低的类型转换成级别高的类型,然后再进行运算,运算结果与其中级别高的操作数的类型相同

4.5.2 强制类型转换

  • 强制类型转换是利用强制类型转换运算符将数据类型转换成所需要的类型。强制类型转换符是由一对圆括号将某个类型名括起来构成的
  • 求余运算符的两个操作数必须是整型数据或字符型数据
  • 自动转换一般不会使数据受到损失,而强制转换就有可能使数据受损或结果难以理解,这是由于高级别的类型转换为低级别的类型时无法完整存储造成的

5.3.1 关系运算符

  • 关系运算符的优先级别比算术运算符的级别低,但比赋值运算符的级别高。
  • 关系运算符的结合方向是从左向右

5.4.1 逻辑运算符

  • 逻辑非的优先级高于逻辑与的优先级,而逻辑与的优先级又高于逻辑或的优先级。

5.5.1 条件运算符

  • 条件运算符的结合方向是自右向左。当一个式子中出现多个条件运算符时,应该将位于最右边的问号与离它最近的冒号配对,并按这一原则正确区分各条件运算符的运算对象。

5.7 位运算符

  • 位运算符分为位逻辑运算符、位移位运算符和位自反赋值运算符三种。位运算对象只能是整型(int、short、unsigned、long)或字符型(char)数据。

6.1.1 流程控制语句

  • break语句常用于使流程跳出switch结构,继续执行switch语句下面的一个语句。
  • loop是一个语句标号,当程序执行到goto loop语句的时候,程序将跳转到loop所标识的那一行程序即if语句。

6.2 输入与输出函数

  • scanf()(格式输入函数)、printf()(格式输出函数)、getchar()(字符输入函数)、putchar()(字符输出函数)、gets()(字符串输入函数)、puts()(字符串输出函数)。

6.2.2 格式输入函数

  • ❑ D:输入有符号的十进制整数;❑ o:输入无符号的八进制整数;❑ u:输入无符号的十进制整数;❑ x或X:输入无符号的十六进制整数;❑ c:输入单个字符;❑ s:输入字符串;❑ f:输入单精度或双精度数。
  • 可以指定输入数据所占的列数,系统自动按指定的列数截取所需的数据。
  • 在上面的程序中,分别为字符型数据a,b指定了输入的字符序列,即将AB给了变量a,将CDE给了变量b,而由于字符型数据只能存储一个字节的数据,因此a=A,b=C。
  • 如果在%后面有个“*” 附加说明符,表示跳过它指定的列数。

10.1 数组的概述

  • C语言在编译的时候会根据所声明的数组大小在内存中开辟一段连续的空间来作为数组的存储地址,因此,在声明时,必须声明数组的长度。

10.2.1 一维数组的定义

  • 不能用变量来表示元素的个数,可以用符号常量和常量表达式。

10.2.3 一维数组的引用

  • 在C语言中,不允许一次引用整个数组,只能逐个引用每个数组元素。

11.4 字符数组与字符串的关系

  • 用字符串作为初值时,字符数组的长度是字符串的长度再加上结束符。

11.6.4 字符串比较函数strcmp

  • 字符串比较不能用关系运算符,只能用strcmp函数,比较结果由strcmp函数返回

12.4.2 函数参数的地址传递

  • 地址传递指的是实际参数将变量的地址传递给形式参数。这样实际参数和形式参数指向同一个内存单元,在调用函数过程中,如果形式参数所指向的内容发生变化,实际参数也会发生变化。

13.5.2 局部变量和全局变量

  • 在同一C程序中,当局部变量与全局变量同名时,在局部变量作用的范围内,全局变量不起作用。

14.1 指针与地址

  • 一个变量所占字节中的第一个字节的地址,称为这个变量的地址。
  • 其实在程序编译过后,系统已经将变量名转换为变量的地址,对变量值的存取操作都是通过地址进行的。

14.2.1 指针变量的定义

  • 无论指针变量指向何种类型数据,指针变量本身是整型的,即指针变量本身也是有地址的,占两个节的存储空间。
  • 在输出语句中用到的*号,表示取内容,*p表示取指针变量p所指向的内容。

14.2.3 指针的运算

  • 同类指针变量相减的结果是这两个指针之间数据元素的个数。

14.3 指针和数组

  • 使用指针法能够使目标程序质量高:占内存少,运行速度高。

14.3.1 数组的指针和指向数组的指针变量

  • C语言中规定:如果指针变量p已经指向数组中的一个元素,则p+1表示指向下一个元素,而不是将p的值简单地加1。
  • 数组名代表数组首元素的地址,它是一个常量,它在程序运行期间是不变的。
  • 请务必牢记:a[i]与*(a+i)是等价的,它们都表示第i行的首元素地址。

14.3.4 数组名作为函数参数

  • 数组名作为函数参数时,传递的是数组的首地址。

15.1.2 结构体类型的定义

  • ❑ 当结构体成员的数据类型选取了自身的结构体时,成员只能是本结构体的指针变量或者是结构体指针型数组,但不能是结构体变量或结构数组。

15.2.1 结构体变量的定义与初始化

  • 结构体变量定义的方式有三种:一是先定义结构体,然后再定义相应结构体变量;二是在定义结构体的同时定义变量;三是直接定义结构体类型变量。