目录
1 介绍
2 基本数据类型
3 字符串
4 运算符与表达式
5 控制语句
6 I/O
1 C语言编译过程
链接器的作用是,把你编写的目标代码、系统的标准启动代码和库代码这 3 部分合并成一个文件,即可执行文件。对于库代码,链接器只会把程序中要用到的库函数代码提取出来。
2 C程序解剖
3 命名规则
给变量命名时要使用有意义的变量名或标识符,如果变量名无法清楚地表达自身的用途,可在注释中进一步说明。
可以用小写字母、大写字母、数字和下划线(_)来命名。而且,名称的第1个字符必须是字符或下划线,不能是数字。
操作系统和C库经常使用以一个或两个下划线字符开始的标识符(如,_kcab),因此最好避免在自己的程序中使用这种名称。
C语言的名称区分大小写,即把一个字母的大写和小写视为两个不同的字符。因此,stars和Stars、STARS都不同。
C99和C11允许使用更长的标识符名,但是编译器只识别前63个字符。对于外部标识符,只允许使用31个字符。
在C语言中,整数是没有小数部分的数。浮点数与数学中实数的概念差不多,2.75、3.16E7、7.00 和 2e-8 都是浮
点数。注意,在一个值后面加上一个小数点,该值就成为一个浮点值。所以,7是整数,7.00是浮点数。
计算机把浮点数分成小数部分和指数部分来表示,而且分开储存这两部分。
int类型是有符号整型,即int类型的值必须是整数,可以是正整数、负整数或零。其取值范围依计算机系统而异。一般而言,储存一个int要占用一个机器字长。目前的个人计算机一般是64位,因此用64位储存一个int值。
C语言提供3个附属关键字修饰基本整数类型:short、long和unsigned。
有符号类型:
short int类型(或者简写为short)占用的存储空间可能比int类型少,常用于较小数值的场合以节省空间。
long int或long占用的存储空间可能比int多,适用于较大数值的场合。
long long int或long long(C99标准加入)占用的储存空间可能比long多,适用于更大数值的场合。该类型至少占64位。
无符号类型:
unsigned int或unsigned只用于非负值的场合。这种类型与有符号类型表示的范围不同。
八进制和十六进制常量被视为int类型。如果值太大,编译器会尝试使用unsigned int。如果还不够大,编译器会依次使用long、unsigned long、longlong和unsigned long long类型。
要把一个较小的常量作为long类型对待,可以在值的末尾加上l(小写的L)或L后缀。类似地,在支持long long类型的系统中,也可以使用ll或LL后缀来表示。另外,u或U后缀表示unsigned。
打印unsigned int类型的值,使用%u转换说明;打印long类型的值,使用%ld转换说明。
C语言只规定了short占用的存储空间不能多于int,long占用的存储空间不能少于int。这样规定是为了适应不同的机器。
int类型那么多,应该如何选择?首先,考虑unsigned类型。这种类型的数常用于计数,因为计数不用负数。而且,unsigned类型可以表示更大的正数。如果一个数超出了int类型的取值范围,且在long类型的取值范围内时,使用long类型。、
如果整数超出了相应类型的取值范围会怎样?可以把无符号整数看作是汽车的里程表。当达到它能表示的最大值
时,会重新从起始点开始。整数 也是类似的情况。它们主要的区别是,在超过最大值时,unsigned int 类型的变量 从 0开始;而int类型的变量则从−2147483648开始。
char类型用于储存字符,但是从技术层面看,char是整数类型。因为char类型实际上储存的是整数而不是字符。计算机使用数字编码来处理字符,即用特定的整数表示特定的字符。
在C语言中,用单引号括起来的单个字符被称为字符常量(character constant)。
char broiled; /* 声明一个char类型的变量 */
broiled = 'T'; /* 为其赋值,正确 */
broiled = T; /* 错误!此时T是一个变量 */
broiled = "T"; /* 错误!此时"T"是一个字符串 */
单引号只适用于字符、数字和标点符号,浏览ASCII表会发现,有些ASCII字符打印不出来。例如,一些代表行为的字符(如,退格、换行、终端响铃或蜂鸣)。C语言提供了3种方法表示这些字符:
第1种方法——使用ASCII码。例如,蜂鸣字符的ASCII值是7,因此可以这样写:
char beep = 7;
第2种方法是,使用特殊的符号序列表示一些特殊的字符。这些符号序列叫作转义序列(escape sequence)。
把转义序列赋给字符变量时,必须用单引号把转义序列括起来。例如,
char beep = '\a';
第三种方法,使用八进制和十六进制
用八进制ASCII码表示一个字符,可以在编码值前面加一个反斜杠(\)并用单引号括起来。
例如,如果编译器不识别警报字符(\a),可以使用ASCII码来代替:beep = '\007';
用十六进制形式表示字符常量,即反斜杠后面跟一个x或X,再加上1~3位十六进制数字。
例如,Ctrl+P字符的ASCII十六进制码是10(相当于十进制的16),可表示为'\x10'或'\x010'。
无论是普通字符还是转义序列,只要是双引号括起来的字符集合,就无需再用单引号括起来。
打印输出:
C标准规定,float类型必须至少能表示6位有效数字,且取值范围至少是10 -37 ~10 +37 。前一项规定指float类型必须至少精确表示小数点后的6位有效。后一项规定用于方便地表示诸如太阳质量(2.0e30千克)。通常,系统储存一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下24位用于表示非指数部分(也叫作尾数或有效数)及其符号。
C语言提供的另一种浮点类型是double(意为双精度)。double类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。一般情况下,double占用64位而不是32位。
浮点型常量的基本形式是:有符号的数字(包括小数点),后面紧跟e或E,最后是一个有符号数表示10的指数。
可以没有小数点(如,2E5)或指数部分(如,19.28),但是不能同时省略两者。
可以省略小数部分(如,3.E16)或整数部分(如,.45E-6),但是不能同时省略两者。
不要在浮点型常量中间加空格:1.56 E+12(错误!)
默认情况下,编译器假定浮点型常量是double类型的精度。
例如,假设some是float类型的变量,编写下面的语句:
some = 4.0 * 2.0;
通常,4.0和2.0被储存为64位的double类型,使用双精度进行乘法运算,然后将乘积截断成float类型的宽度。
C99 标准添加了一种新的浮点型常量格式——用十六进制表示浮点型常量,即在十六进制数前加上十六进制前缀(0x或0X),用p和P分别代替e和E,用2的幂代替10的幂(即,p计数法)。
0xa.1fp10
十六进制a等于十进制10,.1f是1/16加上15/256(十六进制f等于十进制15),
p10是2^10 或1024。0xa.1fp10表示的值是(10 + 1/16 +15/256)×1024(即,十进制10364.0)。
printf()函数使用%f转换说明打印十进制记数法的float和double类型浮点数,用%e打印指数记数法的浮点数。如果系统支持十六进制格式的浮点数,可用a和A分别代替e和E。打印long double类型要使用%Lf、%Le或%La。
C编译器把浮点数转换成整数时,会直接丢弃(截断)小数部分,而不进行四舍五入。
1)当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会被自动转换成int,如有必要会被转换成unsigned int(如果short与int的大小相同,unsigned short就比int大。这种情况下,unsigned short会被转换成unsigned int)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级(promotion)。
2)涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
3)类型的级别从高至低依次是long double、double、float、unsignedlonglong、long long、unsigned long、long、unsigned int、int。例外的情况是,当long 和 int 的大小相同时,unsigned int比long的级别高。之所以short和char类型没有列出,是因为它们已经被升级到int或unsigned int。
4)在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。
5)当作为函数参数传递时,char和short被转换成int,float被转换成double。
如果待转换的值与目标类型不匹配怎么办?
1.目标类型是无符号整型,且待赋的值是整数时,额外的位将被忽略。例如,如果目标类型是 8 位unsigned char,待赋的值是原始值求模256。
2.如果目标类型是一个有符号整型,且待赋的值是整数,结果因实现而异。
3.如果目标类型是一个整型,且待赋的值是浮点数,该行为是未定义的。
如果把一个浮点值转换成整数类型会怎样?当浮点类型被降级为整数类型时,原来的浮点值会被截断。
强制类型转换运算符,在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。
C语言没有专门用于储存字符串的变量类型,字符串都被储存在char类型的数组中。数组末尾位置的字符\0。这是空字符(null character),C语言用它标记字符串的结束。
你不用亲自把空字符放入字符串末尾,scanf()在读取输入时就已完成这项工作。
scanf("%s", name);
如果你输入“Angela Plains”
scanf()只读取了Angela Plains中的Angela,它在遇到第1个空白(空格、制表符或换行符)时就不再读取输入。
因此,scanf()在读到Angela和Plains之间的空格时就停止了。
一般而言,根据%s转换说明,scanf()只会读取字符串中的一个单词,而不是一整句。
字符串常量"x"和字符常量'x'不同。区别之一在于'x'是基本类型(char),而"x"是派生类型(char数组);区别之二是"x"实际上由两个字符组成:'x'和空字符\0。
sizeof 运算符,它以字节为单位给出对象的大小。strlen()函数给出字符串中的字符长度。因为 1 字节储存一个字符,读者可能认为把两种方法应用于字符串得到的结果相同,但事实并非如此。用 strlen()得出的是字符串中的字符数(包括空格和标
点符号)。然而,sizeof运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内。对于早期的C,还要知道sizeof和strlen()返回的实际类型(通常是unsigned或unsigned long)。
sizeof 是否使用了圆括号
圆括号的使用时机否取决于运算对象是类型还是特定量。
运算对象是类型时,圆括号必不可少,对于类型,应写成sizeof(char)或sizeof(float);
但是对于特定量,可有可无。对于特定量,可写成sizeof name或sizeof 6.28。
使用符号常量(symbolic constant):首先,常量名比数字表达的信息更多。另外,假设程序中的多处使用一个常量,有时需要改变它的值。如何创建符号常量?
1)是声明一个变量,然后将该变量设置为所需的常量。可以这样写:
float taxrate;
taxrate = 0.015;
2)C预处理器:#define TAXRATE 0.015
编译程序时,程序中所有的TAXRATE都会被替换成0.015。这一过程被称为编译时替换(compile-time substitution)。
为什么TAXRATE 要用大写?用大写表示符号常量是 C 语言一贯的传统。另外,还有一个不常用的命名约定,即在名称前带c_或k_前缀来表示常量(如,c_level或k_line)。
3)C90标准新增了const关键字,用于限定一个变量为只读,即不可以更改。
C头文件limits.h和float.h分别提供了与整数类型和浮点类型大小限制相关的详细信息。每个头文件都定义了一系列供实现使用的明示常量。
最初,printf()语句把输出发送到一个叫作缓冲区(buffer)的中间存储区域,然后缓冲区中的内容再不断被发送到屏幕上。C 标准明确规定了何时把缓冲区中的内容发送到屏幕:当缓冲区满、遇到换行字符或需要输入的时候(从缓冲区把数据发送到屏幕或文件被称为刷新缓冲区)。
虽然printf()是输出函数,scanf()是输入函数,但是它们的工作原理几乎相同。两个函数都使用格式字符串和参数列表。 格式字符串包含两种形式不同的信息:实际要打印的字符;转换说明。
1) 转换说明(conversion specification),它们指定了如何把数据转换成可显示的形式。
转换说明把以二进制格式储存在计算机中的值转换成一系列字符(字符串)以便于显示。
2) 在%和转换字符之间插入修饰符可修饰基本的转换说明。
一些简单的类型处理:
printf()函数中所有float类型的参数(对未使用显式原型的所有C函数都有效)仍自动转换成double类型。把size_t定义成系统使用sizeof返回的类型,这被称为底层类型(underlying type)。printf()使用z修饰符表示打印相应的类型。同样,C还定义了ptrdiff_t类型和t修饰符来表示系统使用的两个地址差值的底层有符号整数类型。
3) 标记
printf()函数也有一个返回值,它返回打印字符的个数。如果有输出错误,printf()则返回一个负值。在检查输出错误时可能会用到(如,在写入文件时很常用)。
有时,printf()语句太长,在屏幕上不方便阅读。如果空白(空格、制表符、换行符)仅用于分隔不同的部分,C 编译器会忽略它们。
printf("The printf() function printed %d characters.\n",
rv);
该语句在逗号和rv之间断行,C编译器会忽略多余的空白。
但是,不能在双引号括起来的字符串中间断行。如果这样写:
printf("The printf() function printed %d
characters.\n", rv);
C编译器会报错:字符串常量中有非法字符。
在字符串中,可以使用\n来表示换行字符,但是不能通过按下Enter(或Return)键产生实际的换行符。
给字符串断行有3种方法:
方法1:使用多个printf()语句。因为第1个字符串没有以\n字符结束。
方法2:用反斜杠(\)和Enter(或Return)键组合来断行。这使得光标移至下一行,而且字符串中不会包含换行符。
printf("Here's another way to print a \
long string.\n");
方法3:ANSI C引入的字符串连接。在两个用双引号括起来的字符串之间用空白隔开,C编译器会把多个字符串看作是一个字符串。
printf("Hello, young " "lovers" ", wherever you are.");
scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配。
例如,假设在两个转换说明中添加一个逗号:
scanf("%d,%d", &n, &m);
scanf()函数将其解释成:用户将输入一个数字、一个逗号,然后再输入一个数字。
也就是说,用户必须像下面这样进行输入两个整数:88,121
除了%c,其他转换说明都会自动跳过待输入值前面所有的空白。因此,scanf("%d%d", &n, &m)与scanf("%d %d", &n, &m)的行为相同。
如果把%c放在格式字符串中的空格前面,scanf()便会跳过空格,从第1个非空白字符开始读取。也就是说,scanf("%c", &ch)从输入中的第1个字符开始读取,而scanf(" %c", &ch)则从第1个非空白字符开始读取。
scanf()函数返回成功读取的项数。如果没有读取任何项,且需要读取一个数字而用户却输入一个非数值字符串,scanf()便返回0。当scanf()检测到“文件结尾”时,会返回EOF。
printf()的*修饰符。如果你不想预先指定字段宽度,希望通过程序来指定,那么可以用*修饰符代替字段宽度。
scanf()中*的用法与此不同。把*放在%和转换字符之间时,会使得scanf()跳过相应的输出项。
在C语言中,=并不意味着“相等”,而是一个赋值运算符。=号左侧是一个变量名,右侧是赋给该变量的值。=号左侧的项必须是一个变量名。
左值:它指定一个对象,所以引用内存中的地址。如const
可修改的左值:它指定一个对象且可用在赋值运算符的左侧。
右值(rvalue)指的是能赋值给可修改左值的量,且本身不是左值。
整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果是整数。整数除法结果的小数部分被丢弃,这一过程被称为截断(truncation)。
求模运算符%用于整数运算。求模运算符给出其左侧整数除以右侧整数的余数。
递增运算符将其运算对象递增1。该运算符以两种方式出现。第1种方式,++出现在其作用的变量前面,这是前缀模式;第2种方式,++出现在其作用的变量后面,这是后缀模式。
C提供条件表达式(conditional expression)作为表达if else语句的一种便捷方式,该表达式使用?:条件运算符。该运算符分为两部分,需要 3 个运算对象。
条件表达式的通用形式如下:
expression1 ? expression2 : expression3
如果 expression1 为真(非 0),那么整个条件表达式的值与 expression2的值相同;如果expression1为假(0),那么整个条件表达式的值与expression3的值相同。
当且仅当exp1和exp2都为真时,exp1 && exp2才为真;
如果exp1或exp2为真,则exp1 || exp2为真;
如果exp1为假,则!exp1为真;如果exp1为真,则!exp1为假。
C 是在美国用标准美式键盘开发的语言。C99标准新增了可代替逻辑运算符的拼写,它们被定义在ios646.h头文件中。如果在程序中包含该头文件,便可用and代替&&、or代替||、not代替!。
!运算符的优先级很高,比乘法运算符还高,与递增运算符的优先级相同,只比圆括号的优先级低。&&运算符的优先级比||运算符高,但是两者的优先级都比关系运算符低,比赋值运算符高。
除了两个运算符共享一个运算对象的情况外,C 通常不保证先对复杂表达式中哪部分求值。apples = (5 + 3) * (9 + 6);(能先对表达式5 + 3求值,也可能先对表达式9 + 6求值)C 把先计算哪部分的决定权留给编译器的设计者,以便针对特定系统优化设计。
但是,对于逻辑运算符是个例外,C保证逻辑表达式的求值顺序是从左往右。&&和||运算符都是序列点,所以程序在从一个运算对象执行到下一个运算对象之前,所有的副作用都会生效。
C语言运算符优先级:关系运算符的优先级比算术运算符(包括+和-)低,比赋值运算符高。
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
-- |
() |
圆括号 |
(表达式)/函数名(形参表) |
-- |
||
. |
成员选择(对象) |
对象.成员名 |
-- |
||
-> |
成员选择(指针) |
对象指针->成员名 |
-- |
||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
~ |
按位取反运算符 |
~表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
|||
-- |
自减运算符 |
--变量名/变量名-- |
|||
* |
取值运算符 |
*指针变量 |
|||
& |
取地址运算符 |
&变量名 |
|||
! |
逻辑非运算符 |
!表达式 |
|||
(类型) |
强制类型转换 |
(数据类型)表达式 |
-- |
||
sizeof |
长度运算符 |
sizeof(表达式) |
-- |
||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
|||
% |
余数(取模) |
整型表达式%整型表达式 |
|||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
|||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
|||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
|||
< |
小于 |
表达式<表达式 |
|||
<= |
小于等于 |
表达式<=表达式 |
|||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
|||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
-- |
/= |
除后赋值 |
变量/=表达式 |
-- |
||
*= |
乘后赋值 |
变量*=表达式 |
-- |
||
%= |
取模后赋值 |
变量%=表达式 |
-- |
||
+= |
加后赋值 |
变量+=表达式 |
-- |
||
-= |
减后赋值 |
变量-=表达式 |
-- |
||
<<= |
左移后赋值 |
变量<<=表达式 |
-- |
||
>>= |
右移后赋值 |
变量>>=表达式 |
-- |
||
&= |
按位与后赋值 |
变量&=表达式 |
-- |
||
^= |
按位异或后赋值 |
变量^=表达式 |
-- |
||
|= |
按位或后赋值 |
变量|=表达式 |
-- |
||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
-- |
表达式(expression)由运算符和运算对象组成,最简单的表达式是一个单独的运算对象。运算对象可以是常量、变量或二者的组合。
语句(statement)是C程序的基本构建块。一条语句相当于一条完整的计算机指令。在C中,大部分语句都以分号结尾。
复合语句(compound statement)是用花括号括起来的一条或多条语句,复合语句也称为块(block)。
注意,声明不是表达式语句。也就是说,如果删除声明后面的分号,剩下的部分不是一个表达式,也没有值。
术语(副作用和序列点):
1)副作用是对数据对象或文件的修改。给出表达式states = 50,C会对其求值得50。对该表达式求值的副作用是把变量states的值改为50;调用 printf()函数时,它显示的信息其实是副作用。
2)序列点(sequence point)是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。在 C语言中,语句中的分号标记了一个序列点。
1)表达式:
while循环的通用形式如下:
while ( expression )
statement
statement部分可以是以分号结尾的简单语句,也可以是用花括号括起来的复合语句。expression是值之间的比较,可以使用任何表达式。
2)真值:
在C语言中,一直用int类型的变量表示真/假值。C99专门针对这种类型的变量新增了_Bool类型。_Bool类型的变量只能储存1(真)或0(假)。如果把其他非零数值赋给_Bool类型的变量,该变量会被设置为1。这反映了C把所有的非零值都视为真。
C99提供了stdbool.h头文件,该头文件让bool成为_Bool的别名,而且还把true和false分别定义为1和0的符号常量。包含该头文件后,写出的代码可以与C++兼容,因为C++把bool、true和false定义为关键字。
3)循环类型
不确定循环:在测试表达式为假之前,预先不知道要执行多少次循环。while (scanf("%ld", &num) == 1)
计数循环:这类循环在执行循环之前就知道要重复执行多少次。
在创建一个重复执行固定次数的循环中涉及了3个行为:
1.必须初始化计数器;
2.计数器与有限的值作比较;
3.每次循环时递增计数器。
关键字for后面的圆括号中有3个表达式,分别用两个分号隔开。第1个表达式给计数器赋初值,第2个表达式表示计数器的范围,第3个表达式更新计数器。
for圆括号中的表达式也叫做控制表达式,它们都是完整表达式,所以每个表达式的副作用(如,递增变量)都发生在对下一个表达式求值之前。
1)逗号运算符:
逗号运算符扩展了for循环的灵活性,for (ounces = 1, cost = FIRST_OZ; ounces <= 16;ounces++,cost += NEXT_OZ)
逗号运算符有两个其他性质。首先,它保证了被它分隔的表达式从左往右求值;其次,整个逗号表达式的值是右侧项的值。
while循环和for循环都是入口条件循环,即在循环的每次迭代之前检查测试条件,所以有可能根本不执行循环体中的内容。C语言还有出口条件循环(exit-condition loop),即在循环的每次迭代之后检查测试条件,这保证了至少执行循环体中的内容一次。这种循环被称为 do while循环。
do
statement
while ( expression );
statement可以是一条简单语句或复合语句。注意,do while循环以分号结尾。
嵌套循环(nested loop)指在一个循环内包含另一个循环。嵌套循环常用于按行和列显示数据,也就是说,一个循环处理一行中的所有列,另一个循环处理所有的行。
if语句被称为分支语句(branching statement)或选择语句(selection statement).
if ( expression )
statement
如果对expression求值为真(非0),则执行statement;否则,跳过statement。与while循环一样,statement可以是一条简单语句或复合语句。
if ( expression )
statement1
else
statement2
如果要在if和else之间执行多条语句,必须用花括号把这些语句括起来成为一个块。
getchar()和 putchar()每次只处理一个字符,这种方法很适合计算机。而且,这是绝大多数文本(即,普通文字)处理程序所用的核心方法。
getchar()函数不带任何参数,它从输入队列中返回下一个字符。它们通常是预处理宏,而不是真正的函数
ch = getchar();
该语句与下面的语句效果相同:
scanf("%c", &ch);
while ((ch = getchar()) != '\n'),把两个行为合并成一个表达式。
while (ch = getchar() != '\n'),!=运算符的优先级比=高,所以先对表达式getchar() != '\n'求值。由于这是关系表达式,所以其值不是1就是0(真或假)。然后,把该值赋给ch。省略圆括号意味着赋给ch的值是0或1,而不是 getchar()的返回值。
C 有一系列专门处理字符的函数,这些函数接受一个字符作为参数,如果该字符属于某特殊的类别,就返回一个非零值(真);否则,返回0(假)。
3种循环都可以使用continue语句。执行到该语句时,会跳过本次迭代的剩余部分,并开始下一轮迭代。如果continue语句在嵌套循环内,则只会影响包含该语句的内层循环。
程序执行到循环中的break语句时,会终止包含它的循环,并继续执行下一阶段。
在for循环中的break和continue的情况不同,执行完break语句后会直接执行循环后面的第1条语句,连更新部分也跳过。嵌套循环内层的break只会让程序跳出包含它的当前循环,要跳出外层循环还需要一个break。
break语句可用于循环和switch语句中,但是continue只能用于循环中。如果switch语句在一个循环中,continue便可作为switch语句的一部分。
程序根据expression的值跳转至相应的case标签处。然后,执行剩下的所有语句,除非执行到break语句进行重定向。
expression和case标签都必须是整数值(包括char类型),标签必须是常量或完全由常量组成的表达式。如果没有case标签与expression的值匹配,控制则转至标有default的语句(如果有的话);否则,将转至执行紧跟在switch语句后面的语句。
switch ( expression )
{
case label1 : statement1//使用break跳出switch
case label2 : statement2
default : statement3
}
可以有多个标签语句,default语句可选。
1)只读首字母
while (getchar() != '\n')
continue; /* 跳过输入行的其余部分 */
循环从输入中读取字符,包括按下Enter键产生的换行符。注意,函数的返回值并没有赋给ch,以上代码所做的只是读取并丢弃字符。
2)何时使用switch?何时使用if else?
如果是根据浮点类型的变量或表达式来选择,就无法使用 switch。如果根据变量在某范围内决定程序流的去向,使用 switch 就很麻烦,这种情况用if就很方便:
if (integer < 1000 && integer > 2)
但是,如果使用switch,程序通常运行快一些,生成的代码少一些。
早期版本的BASIC和FORTRAN所依赖的goto语句,在C中仍然可用。但是C和其他两种语言不同,没有goto语句C程序也能运行良好。(最好不用)
goto语句有两部分:goto和标签名。标签的命名遵循变量命名规则,如下所示:
goto part2;
要让这条语句正常工作,函数还必须包含另一条标为part2的语句,该语句以标签名后紧跟一个冒号开始:
part2: printf("Refined analysis:\n");
实际上,break和continue是goto的特殊形式。使用break和 continue 的好处是:其名称已经表明它们的用法,而且这些语句不使用标签,所以不用担心把标签放错位置导致的危险。
缓冲分为两类:完全缓冲I/O和行缓冲I/O。
完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是 512 字节和 4096字节。
行缓冲I/O指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
ANSI C和后续的C标准都规定输入是缓冲的。
1)getchar()与 scanf()
getchar()读取每个字符,包括空格、制表符和换行符;而 scanf()在读取数字时则会跳过空格、制表符和换行符。不能把它们混用。
while ((ch = getchar()) != '\n')
{
scanf("%d %d", &rows, &cols);
}
要解决这个问题,程序要跳过一轮输入结束与下一轮输入开始之间的所有换行符或空格。
while ((ch = getchar()) != '\n')
{
if (scanf("%d %d", &rows, &cols) != 2)
break;
while (getchar() != '\n')
continue;
}
文件(file)是存储器中储存信息的区域。不同的系统储存文件的方式不同,如果使用标准 I/O 包,就不用考虑这些差异。因此,可以用 if (ch ==)检查换行符。即使系统实际用的是回车符和换行符的组合来标记行末尾,I/O函数会在两种表示法之间相互转换。
从概念上看,C程序处理的是流而不是直接处理文件。
检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。CP/M、IBM-DOS和MS-DOS的文本文件曾经用过这种方法。内嵌的Ctrl+Z字符。(现代的文本文件不一定有嵌入的Ctrl+Z,但是如果有,该操作系统会将其视为一个文件结尾标记。)
另一种方法是储存文件大小的信息。MS-DOS 及新版DOS,Linux使用这种方法处理二进制文件,因为用这种方法可以在文件中储存所有的字符,包括Ctrl+Z。
在C语言中,用getchar()读取文件检测到文件结尾时将返回一个特殊的值,即EOF(end offile的缩写)。scanf()函数检测到文件结尾时也返回EOF。通常, EOF定义在stdio.h文件中:#define EOF (-1)
while ((ch = getchar()) != EOF) getchar()函数实际返回值的类型是int,所以它可以读取EOF字符。
在Linux中按下Ctrl+D会传输文件结尾信号;在PC中,要按下Ctrl+Z。
流(stream)是一个实际输入或输出映射的理想化数据流。
这意味着不同属性和不同种类的输入,由属性更统一的流来表示。于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
C把输入和输出设备视为存储设备上的普通文件,尤其是把键盘和显示设备视为每个C程序自动打开的文件。stdin流表示键盘输入,stdout流表示屏幕输出。getchar()、putchar()、printf()和scanf()函数都是标准I/O包的成员,处理这两个流。
重定向输入让程序使用文件而不是键盘来输入,重定向输出让程序输出至文件而不是屏幕。
echo_eof < words
<符号是UNIX和DOS/Windows的重定向运算符。该运算符使words文件与stdin流相关联,把文件中的内容导入echo_eof程序。
现在,假设你希望制作一份mywords文件的副本,并命名为savewords。只需输入以下命令即可:
echo_eof < mywords > savewords
下面的命令也起作用,因为命令与重定向运算符的顺序无关:
echo_eof > savewords < mywords
注意:在一条命令中,输入文件名和输出文件名不能相同。
重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。
is 28 12.4
在我们眼中,这就像是一个由字符、整数和浮点数组成的字符串。但是对 C程序而言,这是一个字节流。第1个字节是字母i的字符编码,第2个字节是字母s的字符编码,第3个字节是空格字符的字符编码,第4个字节是数字2的字符编码,等等。