第三章
数据和C
因为在任何区间内(如,1.0 到 2.0 之间)都存在无穷多个实数,所以
计算机的浮点数不能表示区间内所有的值。浮点数通常只是实际值的近似
值。例如,7.0可能被储存为浮点值6.99999
第四章
1、字符串
1、字符串是一个或多个字符的序列,双引号告诉编译器它括起来的是字符串
2、字符串由%s表示
3、字符串的实质是char类型数组
2、const限定符
1、const用于限定一个变量为只读
2、在指针针中若const在*之前表示不能通过指针修改若在 *之后表明限定该指针只能指向该数组
3、printf()函数
1、printf()打印数据的指令要与待打印数据的类型相匹配
2、printf( 格式字符串, 待打印项1, 待打印项2,…);
待打印项1、待打印项2等都是要打印的项。它们可以是变量、常量,甚
至是在打印之前先要计算的表达式。第3章提到过,格式字符串应包含每个
待打印项对应的转换说明。
3、 格式字符串中的转换说明一定要与后面的每个项相匹配
4、由于 printf()函数使用%符号来标识转换说明打印%需要2个%
5、printf()的返回值是其打印输出功能的附带用途,通常很少用到,但在检
查输出错误时可能会用到
6、有时,printf()语句太长,在屏幕上不方便阅读。如果空白(空格、制表
符、换行符)仅用于分隔不同的部分,C 编译器会忽略它们。因此,一条语
句可以写成多行,只需在不同部分之间输入空白即可
7、C编译器会报错:字符串常量中有非法字符。在字符串中,可以使用\n
来表示换行字符,但是不能通过按下Enter(或Return)键产生实际的换行
符
scanf()函数
1、scanf()中的格式 字符串表明字符输入流的目标数据类型
2、如果用scanf()读取基本变量类型的值,在变量名前加上一个&;
如果用scanf()把字符串读入字符数组中,不要使用&。
3、scanf()函数使用空白(换行符、制表符和空格)把输入分成多个字段。
在依次把转换说明和字段匹配时跳过空白
4、scanf()函数所用的转换说明与printf()函数几乎相同。主要的区别是,对
于float类型和double类型,printf()都使用%f、%e、%E、%g和%G转换说
明
5、scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字
符必须与输入字符串严格匹配
第五章
基本运算符
赋值运算符
1、在C语言中,=并不意味着“相等”,而是一个赋值运算符,=号左侧是一个变量名,右侧是赋
给该变量的值,符号=被称为赋值运算符。
加法运算符
1、加法运算符用于加法运算,使其两侧的值相加
2、相加的值(运算对象)可以是变量,也可以是常量
减法运算符
1、减法运算符用于减法运算,使其左侧的数减去右侧的数
2、+和-运算符都被称为二元运算符,即这些运算符需要
两个运算对象才能完成操作
符号运算符
1、减号还可用于标明或改变一个值的代数符号
2、符号运算符是一元运算符
3、一元运算符只需要一个运算对象
乘法运算符
1、用*表示
除法运算符
1、C使用符号/来表示除法。/左侧的值是被除数,右侧的值是除数
2、整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法
的结果是整数
运算符优先级
其他运算符
sizeof运算符和size_t类型
1、,sizeof运算符以字节为单位返回运算对象的大小
2、运算对象可以是具体的数据对象或类型
3、如果运算对象是类型则必须用圆括号将其括起来
4、sizeof 返回 size_t 类型的值
求模运算符
1、求模运算符用于整数运算
2、求模运算符给出其左侧整数除以右侧整数的余数
3、求模运算符只能用于整数,不能用于浮点数
4、求模运算符常用于控制程序流
递增运算符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HVSEhKqa-1647655070605)(FIVE.assets/image-20220111200741095.png)]
递减运算符
与递增运算符同理
表达式
1、表达式由运算符和运算对象组成
2、运算对象可以是常量、变量或二者的组合
3、C 表达式的一个最重要的特性是,每个表达式都有一个值
语句
1、语句是C程序的基本构建块,一条语句相当于一条完整的计算机指令
2、C把末尾加上一个分号的表达式都看作是一条语句
3、复合语句是用花括号括起来的一条或多条语句,复合语句也称为块
类型转换
1.当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会被自动转换成int,如有必要会被转换成unsigned int
2.涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别
3.类型的级别从高至低依次是long double、double、float、unsignedlong 、long、long long、unsigned long、long、unsigned int、int
4.在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型
5.当作为函数参数传递时,char和short被转换成int,float被转换成double
第六章
while
1、while循环的通用形式如下:
while ( expression )
statement
statement部分可以是以分号结尾的简单语句,也可以是用花括号括起来
的复合语句。
2、expression是值之间的比较,可以使用任何表达式。如果expression为真
(或者更一般地说,非零),执行 statement部分一次,然后再次判断
expression。在expression为假(0)之前,循环的判断和执行一直重复进行。
每次循环都被称为一次迭代
3、while循环有一点非常重要:在构建while循环时,必须让测试表达式的
值有变化,表达式最终要为假。否则,循环就不会终止
4、要明确一点:只有在对测试条件求值时,才决定是终止还是继续循环
5、while循环是使用入口条件的有条件循环,若入口条件为假则不会执行循环
6、使用while时,要牢记一点:只有在测试条件后面的单独语句(简单语
句或复合语句)才是循环部分
7、即使while语句本身使用复合语句,在语句构成上,它也是一条
单独的语句
8、对C而言,表达式为真的值是1,表达式为假的值是0。
9、在创建一个重复执行固定次数的循环中涉及了3个行为:
1.必须初始化计数器;
2.计数器与有限的值作比较;
3.每次循环时递增计数器。
for循环
1、for循环把初始化、测试和更新组合在一处。
2、关键字for后面的圆括号中有3个表达式,分别用两个分号隔开。第1个
表达式是初始化,只会在for循环开始时执行一次。第 2 个表达式是测试条
件,在执行循环之前对表达式求值。如果表达式为假(本例中,count大于
NUMBER时),循环结束。第3个表达式执行更新,在每次循环结束时求
值。
3、for循环中的3个表达式可以是不同的变量
4、第1个表达式不一定是给变量赋初值,也可以使用printf(),在执
行循环的其他部分之前,只对第1个表达式求值一次或执行一次
5、for语句使用3个表达式控制循环过程,分别用分号隔开。initialize表达
式在执行for语句之前只执行一次;然后对test表达式求值,如果表达式为真
(或非零),执行循环一次;接着对update表达式求值,并再次检查test表达
式
6、for语句是一种入口条件循环,即在执行循环之前就决定了是否执行循
环。因此,for循环可能一次都不执行
运算符
1、逗号运算符扩展了for循环的灵活性,以便在循环头中包含更多的表达
式
2、整个逗号表达式的值是右侧项的值
3、+= 把右侧的值加到左侧的变量上
-= 从左侧的变量中减去右侧的值
*= 把左侧的变量乘以右侧的值
/= 把左侧的变量除以右侧的值
%= 左侧变量除以右侧值得到的余数
do while
1、采用出口条件循 ,即在循环的每次迭代之后检查测试条件,这保证
了至少执行循环体中的内容一次
2、do
statement
while ( expression );
3、do while循环以分号结尾
嵌套循环
1、嵌套循环指在一个循环内包含另一个循环。嵌套循环常
用于按行和列显示数据,也就是说,一个循环处理一行中的所有列,另一个
循环处理所有的行
第七章
if
1、if ( expression )
statement
2、如果对expression求值为真(非0),则执行statement;否则,跳过
statement
3、即使if语句由复合语句构成,整个if语句仍被视为一条语句
ifelse
1、if ( expression )
statement1
else
statement2
2、如果expression为真(非0),则执行statement1;如果expression为假或
0,则执行else后面的statement2
3、if语句用于选择是否执行一个行为,而else if语句用于在两个行为之间
选择
4、规则是,如果没有花括号,else与离它最近的if匹配,除非最近的if被花
括号括起来
5、从技术角度看,if else语句作为一条单独的语句,不必使用花括号。外
层if也是一条单独的语句,也不必使用花括号。但是,当语句太长时,使用
花括号能提高代码的可读性,而且还可防止今后在if循环中添加其他语句时
忘记加花括号
getchar()和putchar()
1、getchar()函数不带任何参数,它从输入队列中返回下一个字符
2、putchar()函数打印它的参数
3、由于这些函数只处理字符,所以它们比更通用的scanf()和printf()函数更
快、更简洁
4、注意 getchar()和 putchar()不需要转换说明,因为它们只
处理字符
逻辑运算
1、C保证逻辑表达式的求值顺序是从左往右
2、&&和||运算符都是序列点,所以程序在从一个运算对象执行到下
一个运算对象之前,所有的副作用都会生效
2、C 保证一旦发现某个元素让整个表达式无效,便立即停止求值
3、当且仅当expression1和expression2都为真,expression1 && expression2才
为真。如果 expression1 或 expression2 为真,expression1 || expression2 为
真。如果expression为假,!expression则为真,反之亦然
4、&&运算符可用于测试范围
条件运算符
1、expression1 ? expression2 : expression3
2、如果 expression1 为真(非 0),那么整个条件表达式的值与 expression2
的值相同;如果expression1为假(0),那么整个条件表达式的值与
expression3的值相同
Switch
1、可以在switch语句中使用多重case标签
2、程序根据expression的值跳转至相应的case标签处。然后,执行剩下的所
有语句,除非执行到break语句进行重定向
3、expression和case标签都必须是整数值(包括char类型),标签必须是常量或完全由常量组成的表达式)
第八章
缓冲区
1、缓冲分为两类:完全缓冲I/O和行缓冲I/O。完全缓冲输入指的是当缓冲
区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入
中。缓冲区的大小取决于系统,常见的大小是 512 字节和 4096字节。行缓
冲I/O指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所
以在按下Enter键后才刷新缓冲区。
2、ANSI C决定把缓冲输入作为标准的原因是:一些计算机不允许无缓冲
输入
文件
1、当编译储存在名为 echo.c 文件中的程序时,编译器打开echo.c文件并读取其中的内容。当
编译器处理完后,会关闭该文件。其他程序,例如文字处理器,不仅要打
开、读取和关闭文件,还要把数据写入文件
2、从较低层面上,C可以使用主机操作系统的基本文件工具直接处
理文件,这些直接调用操作系统的函数被称为底层 I/O
3、从概念上看,C程序处理的是流而不是直接处理文件
4、流是一个实际输入或输出映射的理想化数据流。这意味着不同属性和不同种类的
输入,由属性更统一的流来表示
重定向
1、重定向的一个主要问题与操作系统有关,与C无关
2、重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数
据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一
个程序和另一个程序
3、使用重定向运算符不能读取多个文件的输入,也不能把输出定向至多个
文件
4、通常,文件名和运算符之间的空格不是必须的
5、如果不使用命令行环境,也可以使用重定向
输入验证
1、main()函数管理程序流,为其他函数委派任务
2、虽然输入流由字符组成,但是也可以设置scanf()函数把它们转换成数
值。简而言之,输入由字符组成,但是scanf()可以把输入转换成整数值或浮
点数值
菜单浏览
1、许多计算机程序都把菜单作为用户界面的一部分
2、当你决定实现这个程序时,就要开始考虑如何让程序顺利运行(顺利运
行指的是,处理正确输入和错误输入时都能顺利运行)
3、C程序把输入作为传入的字节流。getchar()函数把每个字符解释成一个
字符编码。scanf()函数以同样的方式看待输入,但是根据转换说明,它可以
把字符输入转换成数值。许多操作系统都提供重定向,允许用文件代替键盘
输入,用文件代替显示器输出
第九章
函数
1、函数是完成特定任务的独立程序代码单元
2、函数原型)告诉编译器函数的类型;函数调用表明在此处执行函数;
函数定义明确地指定了函数要做什么
3、函数和变量一样,有多种类型。任何程序在使用函数之前都要声明该函
数的类型
4、一般而言,函数原型指明了函数的返回值类型和函数接受的参数类型。
这些信息称为该函数的签名
5、在使用函数之前,要用ANSI C形式声明函数原型
6、关键字return后面的表达式的值就是函数的返回值
7、返回值不仅可以赋给变量,也可以被用作表达式的一部分
8、返回值不一定是变量的值,也可以是任意表达式的值
递归
1、C允许函数调用它自己,这种调用过程称为递归
2、可以使用循环的地方通常都可以使用递归。有时用循环解决问题比较
好,但有时用递归更好。递归方案更简洁,但效率却没有循环高
3、每次函数调用都会返回一次。当函数执行完毕后,控制权将被传
回上一级递归。程序必须按顺序逐级返回递归
4、递归函数中位于递归调用之前的语句,均按被调函数的顺序执
行
5、递归函数中位于递归调用之后的语句,均按被调函数相反的顺序
执行
6、虽然每级递归都有自己的变量,但是并没有拷贝函数的代码。程
序按顺序执行函数中的代码,而递归调用就相当于又从头开始执行函数的代
码
7、递归函数必须包含能让递归调用停止的语句
&运算符
1、一元&运算符给出变量的存储地址。如果pooh是变量名,那么&pooh是
变量的地址
初识指针
1、从根本上看,指针是一个值为内存地址的变量
2、要创建指针变量,先要声明指针变量的类型
3、后跟一个变量名时,&给出该变量的地址
4、后跟一个指针名或地址时,*给出储存在指针指向地址上的值
5、声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操
作对象的大小。另外,程序必须知道储存在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们储存数字却大相径庭
6、类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量
是一个指针。int * pi;声明的意思是pi是一个指针,*pi是int类型
7、*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格
第十章
数组
1、数组由数据类型相同的一系列元素组成。需要使用数组时,通过声明数组告诉编译器数组中内含多少元素和这些元素的类型。编译
器根据这些信息正确地创建数组。普通变量可以使用的类型,数组元素都可以用
2、要访问数组中的元素,通过使用数组下标数(也称为索引)表示数组中
的各元素。数组元素的编号从0开始
3、用以逗号分隔的值列表(用花括号括起来)来初始化数组,
各值之间用逗号分隔。在逗号和值之间可以使用空格
4、有时需要把数组设置为只读。这样,程序只能从数组中检索值,不能把
新值写入数组。要创建只读数组,应该用const声明和初始化数组
5、使用数组前必须先初始化它
6、如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的
项数来确定数组的大小
7、对于一般的初始化,在初始化一个元素后,未初始化的元素都会被设置
为0
8、在使用数组时,要防止数组下标超出边界。也就是说,必须确保下标是
有效的值
多维数组
1、初始化二维数组是建立在初始化一维数组的基础上
2、初始化时也可省略内部的花括号,只保留最外面的一对花括号。只要保
证初始化的数值个数正确,初始化的效果与上面相同。但是如果初始化的数
值不够,则按照先后顺序逐行初始化,直到用完所有的值。后面没有值初始
化的元素被统一初始化为0
3、可以把一维数组想象成一行数据,把二维数组想象成数据表,把三维数
组想象成一叠数据表
指针
1、指针提供一种以符号形式使用地址的方法。因为计算机的硬件指令非常依赖地址,指针在某种程度上把程序员想要传达的指令
以更接近机器的方式表达。因此,使用指针的程序更有效率
2、可以更清楚地定义指向int的指针、指向float的指针,以及指向其他
数据对象的指针
3、指针的值是它所指向对象的地址。地址的表示方式依赖于计算机内部的硬件
4、可以使用指针标识数组的元素和获得元素的值
函数数组指针
1、既然能使用指针表示数组名,也可以用数组名表示指针
2、int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只
能用于声明形式参数。第2种形式intar[])提醒读者指针ar指向的不仅仅
一个int类型值,还是一个int类型数组的元素
3、因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参
数是一个与之匹配的指针
4、一元运算符*和++的优先级相同,但结合律是从右往左,所以start++先
求值,然后才是*start。也就是说,指针start先递增后指向。使用后缀形式
(即start++而不是++start)意味着先把指针指向位置上的值加到total上,然
后再递增指针
5、ar[i]和*(ar+1)这两个表达式都是等价的
6、赋值:可以把地址赋给指针。例如,用数组名、带地址运算符(&)的
变量名、另一个指针进行赋值
7、解引用:*运算符给出指针指向地址上储存的值
8、取址:和所有变量一样,指针变量也有自己的地址和值。对指针而言,
&运算符给出指针本身的地址
9、指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针
相加
10、递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个
元素
11、指针减去一个整数:可以使用-运算符从一个指针中减去一个整数。指
针必须是第1个运算对象,整数是第 2 个运算对象
12、指针求差:可以计算两个指针的差值
第十一章
表示字符串和字符串I/O
1、用双引号括起来的内容称为字符串字面量也叫作字符
串常量。双引号中的字符和编译器自动加入末尾的\0字
符,都作为字符串储存在内存中
2、从ANSI C标准起,如果字符串字面量之间没有间隔,或者用空白字符
分隔,C会将其视为串联起来的字符串字面量
3、如果要在字符串内部使用双引号,必须在双引号前面加上一个反斜杠
4、字符串常量属于静态存储类别这说明如果在函
数中使用字符串常量,该字符串只会被储存一次,在整个程序的生命期内存
在,即使函数被调用多次。用双引号括起来的内容被视为指向该字符串储存
位置的指针。这类似于把数组名作为指向该数组位置的指针
5、定义字符串数组时,必须让编译器知道需要多少空间
6、在指定数组大小时,要确保数组的元素个数至少比字符串长度多1
字符串输入
1、要做的第 1 件事是分配空间,以储存稍后读入的字符串
2、过去通常用fgets()来代替gets(),fgets()函数稍微复杂些,在处理输入方
面与gets()略有不同。C11标准新增的gets_s()函数也可代替gets()。该函数与
gets()函数更接近,而且可以替换现有代码中的gets()。但是,它是stdio.h输
入/输出函数系列中的可选扩展,所以支持C11的编译器也不一定支持它
3、fgets()函数通过第2个参数限制读入的字符数来解决溢出的问题
4、fgets()函数的第2个参数指明了读入字符的最大数量。如果该参数的值
是n,那么fgets()将读入n-1个字符,或者读到遇到的第一个换行符为止
5、如果fgets()读到一个换行符,会把它储存在字符串中。这点与gets()不
同,gets()会丢弃换行符
6、fgets()函数的第3 个参数指明要读入的文件。如果读入从键盘输入的数
据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中
7、空指针(或NULL)有一个值,该值不会与任何数据的有效地址对应。
通常,函数使用它返回一个有效地址表示某些特殊情况发生,例如遇到文件
结尾或未能按预期执行
8、如果gets_s()读到最大字符数都没有读到换行符,会执行以下几步。首
先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换
行符或文件结尾,然后返回空指针。接着,调用依赖实现的“处理函数”(或
你选择的其他函数),可能会中止或退出程序