关于我们
书单推荐
新书推荐
|
C程序设计
本书内容包括三个方面: 1)程序的基本构造, 如程序的三种基本结构、函数、常量、变量等; 2)程序的构造方法, 如函数分解、递归、常见算法等; 3)程序的数据描述, 如数组、结构体、文件等。全书以C语言为载体, 不仅介绍了C语言的基本语法, 更重要的是, 通过C语言的语法元素展示了如何构造出一个个完整的程序。
全书以C语言为载体,不仅介绍了C语言的基本语法,更重要的是通过C语言的语法元素展示了如何构造出一个个完整的程序。
与已有的C语言教材相比,本书存在以下特点:
结构上有所突破。本书不是采用传统的条块式结构,而是采用层次结构,即先介绍一个C语言核心,然后再对这个核心进行扩展。
内容组织上有所创新。将文件的基本读写操作提前,让学生更熟悉C文件操作,强化文件操作的训练,并增强例题的趣味性、综合性和实用性;增加C程序运行机制的介绍,如变量内存分配(变量、指针、静态变量)、内存布局(堆、栈函数调用)、缓冲区等,让学生理解C程序运行的背后计算机在发生什么。
精选例题。将例题分为验证性例题和示范性例题。验证性例题纯粹是为了验证和演示某一个知识点,而示范性例题不仅演示了某一用法,更具有示范意义,值得提炼,值得熟记,利于学生形成自己的算法思维,提高程序设计的综合素质和能力。
强调规范化、工程化的开发。介绍了一些常见的工程实践中的做法,如代码风格、命名、程序计时、软件测试等,并强调软件工程中功能分解、函数封装等原则。
程序设计类课程是计算机科学与技术及相关专业的基础课程,在计算机专业课程体系建设中占有十分重要的地位,对于培养学生的思维、能力和兴趣具有十分重要的作用。本书的定位是*门程序设计类课程的教材。
本书围绕程序设计展开,内容包括3个方面:
程序的基本构造,如程序的3种基本结构、函数、常量、变量等;
程序的构造方法,如函数分解、递归、常见算法等;
程序的数据组织,如数组、结构体、文件等。
全书以C语言为载体,不仅介绍了C语言的基本语法,更重要的是通过C语言的语法元素展示了如何构造出一个完整的程序。
与已有的C语言教材相比,本书具有以下特点:
1) 结构上有所突破
在结构上,本书不是采用传统的条块式结构,即按照知识点一个一个地介绍,而是采用层次结构,即先介绍一个C语言核心,其中包括了C语言*常见的用法,然后再对这个核心进行扩展。实践证明,这种结构更容易被学生接受,教学效果更好。
2) 内容组织上有所创新
在内容组织上,有以下两点创新:
(1) 将文件的基本读写操作提前到输入输出章节(第3章)。这样安排的好处是: 一方面让学生更加熟悉C文件操作,强化了文件操作的训练;另一方面可以增强例题的趣味性、综合性和实用性,更容易引起学生的兴趣。
(2) 增加C程序运行机制的介绍。C语言很多知识点与计算机硬件有着紧密的联系,如变量内存分配(变量、指针、静态变量)、内存布局(堆、栈函数调用)、寄存器(寄存器变量)、缓冲区等,要深入地理解C程序不可避免地涉及基本的计算机系统原理。在本书中,专设一章把这些内容串起来,让学生理解C程序运行的背后计算机在发生什么。
3) 精选例题
本书的例题经过了精心的选择和组织。根据例题的性质,将例题分为验证性例题和示范性例题。验证性例题纯粹是为了验证和演示某一个知识点。例如,在printf函数中用%d、%o和%x等不同格式来输出一个整数,通过这个例子,可以验证不同格式符的不同。而示范性例题不仅演示了某一用法,更具有示范意义,值得提炼,值得熟记。例如,判断一个数是否为素数的例子,毫无疑问需要学生完全掌握。本书中的示范性例题都经过了精心挑选,值得好好学习和品读(为了区分,示范性例题在例题编号后加了一个星号,如例2.6)。本书也注重对例题的分析,尽量让学生通过一个题目掌握一类题目的解法。
4) 强调规范化、工程化的开发
程序设计具有很强的工程化属性,因此本书也一直强调程序设计的规范化和工程化思想,书中介绍了一些常见的工程实践中的做法,如代码风格、命名、程序计时、软件测试等,并强调软件工程中功能分解、函数封装等原则。
另外,为了方便读者查找和复习,本书提供了电子版的例题索引;为了方便教学,本书还提供了电子版的PPT演示文稿。读者可以到清华大学出版社网站免费下载。
本书是在《C语言与程序设计方法》(第2版)(万常选、舒蔚、骆斯文、刘喜平编著)的基础上编写的。其中,第1~5、9、14~15章由刘喜平编写,第6、8、10、13章由万常选编写,第7、11章由舒蔚编写,第12章由骆斯文编写。刘喜平、万常选提出本书的编写大纲,并对全书的初稿进行了修改、补充和总纂。
本书在编写过程中参阅了大量的参考文献,在此对参考书目的作者表示衷心感谢!
由于水平有限,加上编写时间仓促,书中难免会有不少缺点或错误,敬请专家和读者批评指正。
作者
2017年1月
第1章程序设计与软件开发11.1程序设计的基本概念1
1.1.1程序1
1.1.2程序设计语言2
1.1.3程序设计4
1.2算法与数据结构7
1.2.1算法及其特征7
1.2.2算法的结构9
1.2.3算法的描述10
1.2.4数据结构14
1.3软件开发16
1.3.1软件16
1.3.2软件工程16
1.4本章小结17
习题119
第2章C语言概述202.1C语言的发展与特点20
2.1.1C语言的发展20
2.1.2C语言的特点21
2.2一个C程序实例22
2.2.1编写和运行C程序23
2.2.2注释23
2.2.3预处理命令24
2.2.4程序主体24
2.3C语言的字符集与标识符26
2.4数据类型27C程序设计:方法与实践目录2.5常量和变量28
2.5.1常量28
2.5.2变量28
2.6运算符和表达式30
2.6.1运算符30
2.6.2表达式32
2.7输入与输出33
2.7.1输出函数printf33
2.7.2输入函数scanf34
2.8语句36
2.8.1简单语句36
2.8.2语句块36
2.8.3if语句37
2.8.4while语句39
2.8.5for语句39
2.9函数41
2.10编程实践: 代码风格42
2.11本章小结44
习题248
第3章数据类型与输入输出493.1整型50
3.1.1整数的内部表示51
3.1.2整型常量51
3.1.3整数的输出52
3.1.4整数的输入55
3.2浮点型57
3.2.1浮点常量58
3.2.2浮点数的内部表示58
3.2.3浮点数的输出58
3.2.4浮点数的输入60
3.2.5浮点数的比较和计算60
3.3字符型62
3.3.1字符型数据的内部表示62
3.3.2字符常量和变量63
3.3.3字符输出64
3.3.4字符输入65
3.3.5字符处理66
3.4数组68
3.4.1什么是数组68
3.4.2数组的内部表示69
3.4.3数组元素的访问69
3.4.4数组的初始化70
3.5字符串71
3.5.1字符串常量71
3.5.2用字符数组处理字符串72
3.5.3字符串的输出73
3.5.4字符串的输入74
3.6文本文件输入与输出77
3.6.1声明FILE 类型的变量78
3.6.2打开文件78
3.6.3关闭文件79
3.6.4读写文件79
3.7变量的进一步讨论81
3.7.1变量的声明与初始化81
3.7.2限定词const82
3.8编程实践: 命名82
3.9本章小结83
习题386
第4章运算符与表达式894.1运算符与表达式概述89
4.1.1C运算符简介89
4.1.2C表达式简介91
4.2算术运算符和算术表达式91
4.2.1算术运算符91
4.2.2算术表达式92
4.2.3算术表达式的例子93
4.3赋值运算符和赋值表达式93
4.3.1赋值运算符94
4.3.2赋值表达式94
4.3.3复合赋值运算符95
4.4增量减量运算符96
4.5子表达式的求值顺序97
4.6数据类型的转换99
4.6.1隐式类型转换99
4.6.2赋值运算符两侧数据的类型转换100
4.6.3强制类型转换103
4.7逗号运算符和逗号表达式104
4.8本章小结105
习题4107
第5章分支结构1105.1关系运算符和关系表达式110
5.2逻辑运算符和逻辑表达式111
5.2.1逻辑运算符111
5.2.2逻辑表达式112
5.3条件运算符和条件表达式114
5.4C语句概述114
5.5if语句116
5.6switch语句119
5.7应用举例122
5.8本章小结127
习题5128
第6章循环结构与程序设计基本算法1336.1循环结构与控制语句133
6.1.1while语句133
6.1.2for语句134
6.1.3dowhile语句136
6.1.4循环嵌套139
6.1.5流程控制语句(break语句、continue语句和goto语句)140
6.2控制循环的基本方法144
6.2.1通过计数器变量控制循环144
6.2.2通过程序执行的状态控制循环148
6.3穷举算法153
6.4迭代与递推算法157
6.4.1迭代157
6.4.2递推159
6.5程序设计实例162
6.6编程实践: 程序计时171
6.7本章小结172
习题6175
第7章函数与结构化程序设计1807.1函数180
7.1.1为什么要使用函数180
7.1.2函数定义183
7.1.3函数调用188
7.1.4函数原型与函数声明190
7.1.5函数的执行193
7.1.6主调函数与被调函数之间的数据传递195
7.1.7函数设计的思路198
7.2递归调用与递归算法202
7.2.1递归调用的执行过程202
7.2.2递归算法204
7.2.3Hanoi塔问题206
7.3程序的函数分解208
7.4C程序结构217
7.4.1编译预处理命令217
7.4.2全局声明224
7.4.3函数225
7.4.4C程序的逻辑与物理构成225
7.5编程实践: 软件测试227
7.6本章小结229
习题7232
第8章指针与数组2368.1指针与指针变量237
8.1.1指针的概念237
8.1.2指针变量的声明与初始化239
8.1.3指针的基本运算241
8.2数组的指针246
8.2.1一维数组的指针246
8.2.2二维数组251
8.2.3二维数组的元素指针和行指针256
8.2.4指向一维数组的指针变量(行指针变量)260
8.3字符指针与字符串262
8.3.1字符串处理函数262
8.3.2指向字符的指针变量处理字符串265
8.4指针作为函数参数267
8.4.1变量的指针作为函数参数267
8.4.2一维数组的指针作为函数参数270
8.4.3二维数组的指针作为函数参数273
8.5返回指针的函数275
8.6指针数组277
8.6.1指针数组的概念及其应用277
8.6.2指针数组作main函数的形参280
8.6.3行指针数组282
8.7编程实践: 实用字符串处理284
8.8本章小结288
习题8297
第3章
数据类型与输入输出
学
习
目
标 了解C语言数据类型的分类。
掌握整型数据的内部表示、整型常量的表示、整型数据的输出和输入。
理解int、short和long 3种整型数据类型的区别。
掌握浮点常量的表示、浮点数的内部表示、浮点数的输出和输入。
理解float和double两种数据类型的区别。
掌握字符型常量和变量的表示和声明、字符型数据的内部表示、字符的输出和输入,掌握常见的字符处理技巧。
理解数组的内部表示,掌握数组元素的访问和初始化。
理解如何表示字符串常量,如何用数组表示来存储和处理字符串。
掌握字符串的输出和输入。
掌握如何向文本文件中输入和输出信息。
C语言是一种强类型语言,也就是说,所有的数据都是具有某种数据类型的,而且必须先声明后使用。C语言提供的数据类型非常丰富,C语言除了提供整型、字符型和浮点型等基本数据类型外,还提供数组、结构体、共用体和指针等数据类型。利用这些数据类型能方便地描述较复杂的数据对象。
C语言的数据类型分类如图3.1所示。
图3.1C语言的数据类型分类
本章将介绍基本类型,其他类型将在后面章节介绍。C程序设计:方法与实践第3章数据类型与输入输出3.1整型
整型是表示整数的数据类型。为了表示不同范围的整数,C语言提供了丰富的整型类型,它们有的可以表示高达19位数的整数,有的只能表示5位数的整数;有的可以表示有符号数,如-23、-98,有的只能表示无符号的数,如1、917。
C语言中的整型类型可以总结成表3.1。其中括号内的内容可以省略。表3.1C语言整型类型
有符号型(默认)说明无 符 号 型说明(signed) int基本整型unsigned int无符号基本整型(signed) short (int)短整型unsigned short (int)无符号短整型(signed) long (int)长整型unsigned long (int)无符号长整型(signed) long long (int)unsigned long long (int)C整型类型分为有符号(signed)和无符号(unsigned)两大类,分别表示有符号数和无符号数。对于有符号数,存储单元的*高位用来存储符号,0表示+,1表示-。对于无符号数,存储单元中全部二进制位都用来表示值,而不包括符号。无符号型变量只能存放不带符号的整数,如23、507等,而不能存放负数,如-23、-98。在默认情况下,整型是有符号的,如果要表示无符号整型,需要显式地加上unsigned来限定。
C标准没有具体规定以上各类数据所占内存字节数(也称为宽度),各种平台上有所不同,但是遵循以下原则: long型数据的字节数应不小于int型,short型不长于int型。例如,对于Win32平台,在Visual Studio编译系统中,各整型类型宽度和取值范围如表3.2所示。在本书中,假定整型数据的规格(宽度、取值范围)与表3.2保持一致。表3.2整型类型的规格
类型所占字节取 值 范 围int4-2 147 483 648~2 147 483 647,即-231~231-1short2-32 768~32 767,即-215~215-1long4-2 147 483 648~2 147 483 647,即-231~231-1long long8-9 223 372 036 854 775 808~9 223 372 036 854 775 807,
即-263~263-1unsigned int40~4 294 967 295,即0~232-1unsigned short20~65 535,即0~216-1unsigned long40~4 294 967 295,即0~232-1unsigned long long80~18 446 744 073 709 551 615,即0~264-1long long和unsigned long long是在C99标准中引入的,目前主流的编译器都支持,但是旧的编译器可能不支持,如Visual C++ 6.0,Turbo C 2.0/3.0。具体到某一个平台和编译系统,可以用sizeof()运算符来获取某一种数据类型或变量的宽度。其用法是在括号中写需要获取宽度的类型名或变量名。例如:
printf("%d", sizeof(int));/输出int型的宽度/
或者
int a;
printf("%d", sizeof(a));/输出int型变量a的宽度/
都可以输出int型的宽度。
3.1.1整数的内部表示
在计算机内部,数据都以二进制形式存在。那么整数在内存中是如何表示的呢?
无符号整数的表示比较简单,直接采用整数的二进制表示。有符号数的*高位用于表示符号位,用0表示+,1表示负号。但是,剩余的二进制位并不是二进制表示。从原理上来说,有符号整数在内部采用补码表示。对于一个数,计算机要使用一定的编码方式进行存储。原码、反码、补码是机器存储一个整数的编码方式。
① 原码。原码就是符号位加上整数的绝对值,即用*位表示符号,其余位表示值。原码是人脑*容易理解和计算的表示方式。
② 反码。反码的表示方法是: 正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
③ 补码。补码的表示方法是: 正数的补码就是其本身;负数的补码是在反码的基础上加1。
例如,如果用8位二进制表示整数,那么+1=(00000001)原码=(00000001)反码=(00000001)补码
-1=(10000001)原码=(11111110)反码=(11111111)补码图3.2给出了几个例子,其中左边是高位,右边是低位。图3.2整数的内部表示示例
为什么int型整数可以表示-231呢?这要从引入补码的原因说起。补码有一个独特的特征,即 a补码+b补码=(a+b)补码。以8位二进制为例,
1补码+(-1)补码= 0 0000001+1 1111111=0 0000000=0补码
8位二进制可以表示-128,可以认为这样产生的:
(-128)补码=(-1)补码+(-127)补码=1 1111111+1 0000001=1 0000000
3.1.2整型常量
整型常量即整型常数。C语言整型常数可用3种表示方式:
十进制整数。如124、234、-23、0等。
八进制整数。以0开头的数是八进制数。如0234表示八进制数(234)8,它等于十进制数156。
十六进制整数。以0x或0X开头的数是十六进制数。如0x234表示十六进制数(234)16,它等于十进制数564。注意,十六进制数只能由数字0~9和字母a~f(或A~F)组成。
当程序中出现整型常量时,如果它属于int类型的取值范围,那么编译器会自动将它当作int型整数来处理,否则作为更宽的数据类型来处理。为了显式地要求编译器把一个常量作为long型处理,可以在后面加一个字母L(或l),如30L、05647L、0x8abfl。为了指明是无符号常量,可以在后面加上字母U(或u),如30U、05647U、0x8abfu。还可以同时加上U和L表示无符号长整型,如30LU、05647LU、0x8abfLU。
3.1.3整数的输出
整数用printf进行格式化输出。printf函数的一般调用格式为: printf(格式化字符串, 输出参数列表);
其中,格式化字符串是用双引号括起来的字符串,它包括两种信息:
① 格式说明,由%和格式字符组成,如%d、%f等。它的作用是将输出的对象采用指定的格式输出。格式说明总是由%字符开始的。
② 普通字符,即需要原样输出的字符,它可以是一般字符,也可以是转义字符。
在用printf函数输出一个整数时,要考虑两个因素: 整数的类型(int、short、long还是long long)和以什么形式输出(什么进制,有符号还是无符号)。
可用的格式控制符如表3.3所示,它们分为两组,一组指示以什么形式输出,另一组告诉printf函数该数据是什么类型。这些格式控制符的组合如表3.4所示。表3.3printf格式字符
作用格式字符(%)说明输出形式d以带符号的十进制形式输出整数(正数不输出符号)o以无符号八进制形式输出整数(不输出前导符0)x以无符号十六进制形式输出整数(不输出前导符0x)u以无符号十进制形式输出整数数据类型h用于短整型l用于长整型ll用于long long类型表3.4printf格式字符的组合
数据类型输 出 形 式duox十进制形式
输出int无符号十进制形式
输出unsigned int无符号八进制形式
输出unsigned int无符号十六进制形式
输出unsigned inth十进制形式
输出short无符号十进制形式
输出unsigned short无符号八进制形式
输出unsigned short无符号十六进制形式
输出unsigned shortl十进制形式
输出long无符号十进制形式
输出unsigned long无符号八进制形式
输出unsigned long无符号十六进制形式
输出unsigned longll十进制形式
输出long long无符号十进制形式
输出unsigned long long无符号八进制形式
输出unsigned long long无符号十六进制形式
输出unsigned long long说明:
① 在选择格式控制符的时候,要根据原本的数据类型和期望的输出形式(十进制、八进制还是十六进制)来确定,尤其是数据类型说明符(h、l、ll)。
② 这些格式控制符只是指示printf函数如何解读、输出整数值,不会影响整数值原本的数据类型和存储形式。
③ 同一个值用不同的格式控制符输出的时候,结果不同,不是值发生了变化,而是对值的解读不同。
④ 用u、o和x格式控制符的时候,将数据解读为无符号数,本应用于输出无符号数,但是也可以用于有符号数,前提是这种解读不会造成曲解。C语言中任何数据都属于某种数据类型,而且任何一个值的数据类型编译系统都是知道的,那么printf函数的格式化字符串中为什么还要有h、l、ll这些与数据类型相关的格式控制字符呢?其实,这些格式控制字符只是告诉系统如何来看待后面的值,也就是说,同一个值可以“当作”不同类型的值来输出。例3.1整型数据的格式化输出。
1#include
2int main() {
3 int a_int=2, b_int=-2;
4 unsigned int c_uint=4294967293;
5 short d_short=2;
6 long e_long=4294967294;
8 printf("a_int: %d, %u, %o, %x\\n", a_int, a_int, a_int, a_int);
9/a_int: 2, 2, 2, 2/
10 printf("b_int: %d, %u, %o, %x\\n", b_int, b_int, b_int, b_int);
11/b_int: -2, 4294967294, 37777777776, fffffffe/
12 printf("a_int: %ld, %lu, %lo, %lx\\n", a_int, a_int, a_int, a_int);
13/a_int: 2, 2, 2, 2/
14 printf("c_uint: %d, %u\\n", c_uint, c_uint);
15/c_uint: -3, 4294967293/
16 printf("d_short: %d, %u\\n", d_short, d_short);
17/d_short: 2, 2/
18 printf("d_short: %hd, %hu\\n", d_short, d_short);
19/d_short: 2, 2/
20 printf("e_long: %hd, %hu\\n", e_long, e_long);
21/e_long: -2, 65534/
22 return 0;
23}
为了方便对照,每一个printf语句的输出结果显示在下一行的注释中。
对比变量的原始值,可以分析如下:
第8行变量a_int用%u、%o和%x 3种格式符输出的时候没有错误,虽然这3种格式本用来输出无符号数。这是因为a_int为正数,以无符号数来解读其内部表示的时候值不变。
第10行变量b_int用%u、%o和%x 3种格式符输出的时候出现错误,因为b_int为有符号数,以无符号数来解读其内部表示的时候得到的是不同的值。
第12行将变量a_int以长整型的形式输出,结果没有问题,因为在Win32平台上int和long两个数据类型是完全相同的。
第14行变量c_uint用%d格式符输出的时候出现问题,因为%d格式符是按有符号数输出,而c_uint的*高为1,被解读为一个负数。
第16行变量d_short用%d和%u输出的时候没有错误。这是因为short型数据会被自动提升为int型。实际上,对于short型数据,既可以用%d、%u也可以用%hd和%hu来输出。
第20行变量e_long用%hd和%hu输出的时候存在问题,因为它被当作short类型(2B)来解读,也就是说,只会考察低位的两个字节,这两个字节的内容按照有符号数来解读是-2,按照无符号数来解读是65534。
上面所用的格式符都是按数据的实际长度输出,为了输出排列的需要,有时要指定每一个数据的输出宽度和对齐方式。指定输出宽度和对齐方式需用到两个附加格式符m和-。附加格式符放在%和格式符之间使用。
m为一正整数,用来指定输出宽度,如果数据的实际宽度比指定输出宽度小,则补上空格后按指定宽度输出;如果数据的实际宽度比指定输出宽度大,则按实际宽度输出。
附加格式符“-”用来说明采用左对齐方式,没有“-”时默认是右对齐方式。
例3.2整型数据按照指定宽度和对齐方式输出。
1#include
2int main(){
3int a=4, b=45, c=456, d=4567;
4unsigned u=456;
5long l=456;
6printf("a=%3d, b=%3d, c=%3d, d=%3d\\n", a, b, c, d);
7/a=4, b=45, c=456, d=4567/
8printf("a=%-3d, b=%-3d, c=%-3d, d=%-3d\\n", a, b, c, d);
9/a=4, b=45 , c=456, d=4567/
10printf("u=%-5u, u=%5u, l=%-5ld, l=%5ld\\n", u, u, l, l);
11/u=456, u=456, l=456, l=456/
12return 0;
13}
其中,每一个printf语句的输出结果显示在下一行的注释中。
3.1.4整数的输入
可以用scanf函数来输入数据给整型变量。回忆一下,scanf函数调用的一般形式为: scanf(格式化字符串, 地址列表);
其中,格式化字符串的含义同printf函数类似,地址列表是由若干个地址组成的列表,可以是变量的地址,或字符串的首地址。
在介绍更多的例子之前,先介绍scanf函数的原理。
scanf函数以及其他的标准输入函数并不是直接从输入设备(如键盘)读取数据,而是从内存中的输入缓冲区中读取数据。如果scanf函数要读取数据,而内存缓冲区为空,scanf函数就会被阻塞,等待用户输入。用户输入数据并按回车键后,所输入的内容才送到内存输入缓冲区中。
scanf函数的*个参数格式化字符串指示了如何从输入缓冲区中读取数据。格式化字符串中可以包含以下内容:
格式化字符,由%引导的字符。格式化字符导致scanf读入若干字符并将其转换为某种类型的数据,该数据会被存入到地址列表中的某一地址。
除%外的非空白字符。一个非空白字符导致scanf读入一个相同的字符但并不存储该字符。如果scanf读不到一个相同的字符,scanf会中断。
空白字符,包括空格、跳格('\\t')和换行符('\\n')。一个空白字符导致scanf读入后面连续的空白符,直到下一个非空白符。
scanf使用的具体格式符与printf中类似,如表3.5所示。表3.5scanf整数输入格式符
作用格式字符(%)说明输入形式d期望读入一个十进制数o期望读入一个八进制数x期望读入一个十六进制数u期望读入一个无符号数数据类型h期望读入一个 short型数据l期望读入一个long型数据ll期望读入一个long long型数据附加格式符m(正整数)指定输入数据所占的宽度(列数)表示本输入项在读入后不赋给相应的变量下面是scanf在使用时需要注意的几个地方。
(1) 输入数据的格式注意要与scanf格式化字符串的格式一致。例如:
……
你还可能感兴趣
我要评论
|