资源描述
第三章 数据和C变量与常量数据有些数据可以在程序使用之前预先设定并在整个运行过程中没有变化,这成为常量,另外的数据在程序运行过程中可能变化或被赋值,这称为变量。变量与常量的区别在于,变量的值可以在程序执行过程中变化与制定,而常量则不可以。原来K&R关键字C90关键字C99关键字intsigned_Boollongvoid_Complexshort_Imaginaryunsignedcharfloatdoubleint 类型int关键字提供了C使用的基本的整数类型。下面三个关键字(long、short和unsigned)以及ANSI附加的signed用于提供基本类型的变种。char关键字用于表示字母以及其他字符(如#、$、%和*)。char类型也可以表示小的整数。float、double和组合long double表示带有小数点的数。_Bool类型表示布尔值(true和false)。_Complex和_Imaginary分别表示复数和虚数。这些类型可以按其在计算机中的存储方式被划分为两个系列。即整数类型和浮点数类型。int类型是有符号整数,即int类型的值必须是整数。16位int取值范围-32768到32767.32位int取值范围-2147483647到2147483647。声明int变量int erns;初始化变量errns = 2;2、21、32、14等都是整数常量,C把不含小数点和指数的数当作是整数。您必须保证格式说明符的数目同待打印值的数目相同,编译器不会发现这种类型的错误。例如:printf (%d %d,hogs);,会使用内存中任意值为第二个%d提供打印值。(因为pirntf()可以有1个、2个、3个或者更多的参数,这使得编译器无法使用常规的方法检查错误。)C语言中,前缀0x或者0X表示使用十六进制值。前缀0表示使用八进制数。例如,十进制数16用八进制数表示为020。short int 简写shortlong int简写 longlong long int简写 long longunsigned int简写 unsignedunsigned long int简写unsigned intunsigned short int简写unsigned short使用多种整数类型的原因为什么说long和short类型可能占用比int类型更多或者更少的存储空间呢?因为C仅保证short类型不会比iint类型长,并且long类型不会比int类型短。这样做是为了适应不同的机器。char类型char类型用于存储字母和标点符号之类的字符。但是在技术实现上char却是整数类型,这是因为char 类型实际存储的是证书而不是字符。标准ASCII码值的范围从0-127,只需7位即可表示。推荐使用字符常量,而不是数值编码。(A ,65)转义字符序列意义a警报(ASCII C)b退格f走纸n换行r回车t水平制表符v垂直制表符反斜杠()单引号()双引号()?问号(?)0oo八进制值(o表示一个八进制数字)xhh十六进制值(h表示一个十六进制数)百分号用%输出给一个字符变量进行赋值时,转义序列必须用单引号括起来.整数常量示例类型十六进制八进制十进制char 0x410101int 0x41010165unsigned int 0x41u0101u65ulong0x41L0101L65Lunsigned long 0x41UL0101UL65ULlong long0x41LL0101LL65LLunsigned long long0x41ULL0101ULL65ULL使用ASCII码时要注意数字和数字字符的区别.例如,字符4的ASCII码值为52,写法4表示符号4而不是数值4。无论普通字符还是转义字符,如果作为双引号中字符集合的一部分,则无需单引号.float,double和long double类型.C语言中浮点数包括float、double和long double类型。C标准规定,float类型必须至少能表示6位有效数字,取值范围至少为10-37到10+37.6位有效数字指浮点数至少应能精确表示像33.333333这样的数字的前6位。取值范围的这一规定使您可以方便的表示诸如太阳的质量(2.0e30千克)、质子的电量(1.6e-19库仑)以及国家债务之类的数字。通常,系统使用32位存储一个浮点数。其中8位用于表示指数及其符号,24位用于表示非指数的部分(称为位数或有效数字)及其符号。C还提供一种称为double(意为双精度)的浮点类型。double类型和float类型具有相同的最小取值范围要求,但它必须至少能表示10位有效数字。一般的,double使用64位而不是32位长度。C提供了第三种浮点类型 long double类型,以满足比double类型更高的精度需求。不过,C只保证long double类型至少同double类型一样精确。浮点常量书写浮点常量有很多选择。一个浮点常量最基本的形式是:包含小数点的一个带符号的数字序列,接着是字母e或E,然后是代表10的指数的一个有符号值。-1.56E+12 2.87e-3。可以省略正好。可以没有小数点(2E5)或者指数部分(19.28),但是不能同时没有二者。可以省略纯小数部分(3.E16)或者整数部分(.45E-6),但是二者不能同时省略(那样做会什么也不会剩下)。下面是更多的有效浮点常量。3.14159、.2、4e16、.8E-5、100.,在浮点常量中不要使用空格(1.56 E+12 错误).默认情况下,编译器将浮点常量当做double类型,例如,假设some是一个float变量,您有下面的语句some = 4.0 * 2.0;那么4.0和2.0被存储为double类型,(通常)使用64位进行存储。乘积运算使用双精度,结果被截为正常的float长度。这能保证计算精度,但是会减慢程序的执行。C使您可以通过f或F后缀使编译器把浮点常量当做float类型,比如2.3f和9.11E9F。l或L后缀使一个数字成为long double类型。建议使用L后缀,因为字母l和数字1容易混淆。没有后缀的浮点常量为double类型。printf(Type double has a size of %u bytes.nsizeof(double);printf()语句被分成两行,只要不在括号内部或一个单词中间进行断行,就可以这样使用。使用数据类型很多程序员和组织都有系统化的变量命名规则,其中变量的名字可以表示它的类型。例如:使用i_前缀表示int变量,使用us_表示unsigned short变量。这样通过名字就可以确定变量i_smart为int类型,变量us_verysmart为unsigned short类型。标准C规定在以下几种情况下将缓冲区内容传给屏幕:缓冲区满的时候、遇到换行符的时候以及需要输入的时候。将缓冲区内容传送给屏幕或文件称为刷新缓冲区(flushing the buffer)。第四章 字符串和格式化输入/输出转换说明符及作为结果的打印输出%a浮点数、十六进制数字和p-记数法(C99)%A浮点数、十六进制数字和P-记数法(C99)%cchar%d有符号十进制数 int%e浮点数、e-记数法%E浮点数、E-记数法%f浮点数、十进制记数法%g根据数值不同自动选择%f或%e. %e格式在指数小于-4或者大于等于精度使用%G根据数值不同自动选择%f或%E. %E格式在指数小于-4或者大于等于精度使用%i有符号十进制整数(与%d相同)%o无符号八进制数%p指针%s字符串%u无符号十进制整数%x使用十六进制数字0f的无符号十六进制整数%X使用十六进制数字0F的无符号十六进制整数%打印一个百分号%ll或者%LLlong long%Lf %Le %Lalong double%hdshort%llongprintf()的标志标志意义-项目是左对齐的;也就是说,会把项目打印在字段的左侧开始处。(右补空格)示例:%-20s+有符号的值若为正,则显示带加号的符号;若为负,则带减号的符号(左布空格)。示例:%+6.2f(空格)有符号的值若为正,则显示时代前导空格(但是不显示符号);若为负,则带减号符号。+标志会覆盖空格标志示例:%6.2f#使用转换水明的可选形式。若为%o格式,则以0开始;若为%x和%X格式,则以0x或0X开始。对于所有的浮点形式,#保证了即使不跟任何数字,也打印一个小数点字符。对于%g和%G格式,它防止尾随零被删除。示例:%#o、%#8f和%+#10.3E0对于所有的数字格式,用前导零而不是用空格填充字段宽度。如果出现-标志或者指定了精度(对于整数)则忽略该标志。示例:%010d和%08.3f打印较长字符串的方法方法1是使用多个printf()语句方法2是使用反斜线符号(/)和回车键的组合来结束第一行方法3采用字符串连接的方法,它是ANSI C的新方法。如果在一个用双引号引起来的字符串后面跟有另一个用双引号引起来的字符串,而且二者之间禁用空白字符分隔,那么C会把该组合当作一个字符串来处理,您应该在字符串内部包含所有必须的空格。scanf()函数使用指向变量的指针。ANSI C中scanf()的转换说明符%c把输入解释成一个字符%d把输入解释成一个有符号十进制数%e %f %g %a把输入解释成一个浮点数(%a是C99标准)%E %F %G %A把输入解释成一个浮点数(%A是C99标准)%i把输入解释成一个有符号十进制整数%o把输入解释成一个有符号八进制整数%p把输入解释成一个指针(一个地址)%s把输入解释成一个字符串:输入的内容以第一个非空白字符开始,并且包含直到下一个空白字符的全部字符%u把输入解释成一个无符号十进制整数%x %X把输入解释成一个有符号十六进制整数scanf()的转换修饰符修饰符*滞后赋值digit(s)最大字段宽度;在达到最大字段宽度或者遇到第一个空白字符时(不管哪个先发生都一样)停止对输入项的读取示例:%10shh把整数读作signed char或unsigned char示例:%hhd %hhull把整数读作long long或者unsigned long long(C99)示例:%lld %lluh,l或L%hd和%hi指示该值将会存储在一个short int汇总。%ho %hx和%hu指示该值将会存储在一个unsigned short int中。%ld和%li指示该值将会存储在一个long中。%lo %lx和%lu指示该值将会存储在一个unsigned long中。%le %lf和%lg指示该值以double类型存储。将L(而非l)与e、f和g一起使用指示该值以long double类型存储。如果没有这些修饰符,d 、i、o和x指示int类型,而e、f和g指示float类型scanf()怎样读入.假设您使用了一个%d说明符来读取一个整数.scanf()函数开始每次读取一个输入字符,它跳过空白字符(空格、制表符和换行符)直到遇到一个非空白字符。如果您使用了字段宽度,那么scanf()在字段结尾或者在第一空白字符处(二者中最先到达的一个)终止。如果第一个非空白字符不是数字,将会发生什么?比如说,是A而非一个数字?这时scanf()会停在哪里,并把A(或者不管是什么)放回输入。没有把任何值赋给指定的变量,程序下一次读取输入时,它就在A处重新开始。如果程序中只有%d说明符,scanf()永远也不会越过哪个A(去读下一个).而且,如果您使用带有多个说明符的scanf()语句,ANSI C要求函数在第一个出错的地方停止读取输入。如果使用%s说明符,呢么空白字符以外的所有字符都是可以接受的,所以scanf()跳过空白字符直到遇到第一个非空白字符,然后保存再次遇到空白字符之前的所有非空白字符。这就意味着%s使scanf()读取一个单词,也就是说,一个不包含空白字符的字符串。如果使用字符宽度,scnaf()在字符的结尾或者第一个空白字符处停止。不能通过字段宽度使得scanf()用以个%s说明符读取多于一个字的输入。最后一点:当scanf()把字符串放在一个指定的数组中,它添加终止的0使得数组内容成为一个C字符串.如果使用%c说明符,那么所有的输入字符都是平等的.如果下一个输入字符是一个空格或者换行符,将会把这个空格或换行符赋给指定的变量;不会跳过空白字符.实际上,scanf()不是C最常用的输入函数,getchar()和gets()更适用于专门的任务,例如读取一个字符或者读取包含空格的字符串.scanf()的返回值scanf()函数返回成功读入的项目的个数.如果它没有读取任何项目(当它期望一个数字而您却键入了一个非数字字符串时就会发生这种情况),scanf()会返回值0.当它检测到文件结尾(end of file)时,它返回EOF.第五章 运算符 表达式和语句基本运算符赋值运算符:=符号=的左边是一个变量名,右边是赋给该变量的值。符号=被称为赋值运算符(assignment operator)。像2002=bmw;这样的语句在C中是没有意义的(确切说是无效的),原因是2002只是一个常量。您不能将一个值赋给一个常量;哪个常量本身就是他的值。所以,当您准备键入代码时请记住在符号=左边的项目必须是一个变量的名字。实际上,赋值运算符必须指向一个存储位置。最简单的方法是使用变量的名字,但是以后您看到,指针也可以用于指向一个存储位置。更普遍的,C使用术语可修改的左值(modifiable lvalue)来标志那些我们可以为止赋值的试题。C的术语左值(lvalue)指用于标识一个特定的数据对象的名字或表达式。例如,变量的名字是一个左值。所以对象指的是实际的数据存储,但是左值是用于识别或定位哪个存储的标识符。术语右值(rvalue)指的是能赋给可修改的左值的量。加法运算符 +减法运算符 符号运算符 - +乘法运算符 *除法运算符 /整数的除法运算和浮点型数的除法运算有很大的不同。浮点类型的除法运算得出一个浮点数结果,而整数除法运算则产生一个整数的结果。整数不能有小数部分。在C中,整数除法结果的小数部分都被丢弃。这个过程被称为截尾(truncation)。没有把整数除法运算的结果四舍五入到最近的整数,而是进行截尾,即舍弃整个小数部分。其他运算符sizeof运算符和size_t类型sizeof运算符以字节为单位返回其操作数的大小。操作数可以是一个具体的数据对象(例如一个变量名),或者一个类型。C规定sizeof返回size_t类型的值。这是一个无符号的整数类型,但它不是一个新类型。C99把%zd作为用来显示size_t类型的printf()说明符。取模运算符 %取模运算符(modulus operator)用于整数运算。该运算符计算出用它右边的整数除它左边的整数得到的余数。不要对浮点数使用该运算符,那将是无效的。增量和减量运算符: + -增量运算符(increment operator),将其操作数的值增加1.如果您企图一次使用太多的增量运算符,可能连自己都会被弄糊涂。通过如下原则,您可以很容易避免这些问题:如果一个变量出现在同一个函数的多个参数中时,不要将增量或者减量运算符用于它上面。当一个变量多次出现在一个表达式里时,不要将增量或减量运算符用到它的上面。关系运算符运算符含义小于=大于或等于大于!=不等于表达式和语句表达式(expression)是由运算符和操作数组合构成的(回忆一下,操作数是运算符操作的对象)。最简单的表达式是一个单独的操作数,以此作为基础可以建立复杂的表达式。例如:4,-6, q 3。操作数可以使常量、变量或者二者的组合。一些表达式是多个较小的表达式的组合,这些小的表达式被称为子表达式(subexpression)。每一个表达式都有一个值语句语句(statement)是构造程序的基本成分。程序是一系列带有某种必须的标点的语句集合。一个语句是一条完整的计算机指令。在C中,语句用结束处的一个分号标识。什么构成了一条完整的指令?首先C把任何后面加有一个分号的表达式看作是一个语句(它们被成分为表达式语句)。尽管一个语句(或者至少是一个有作用的语句)是一条完整的指令,但不是所有的完整的指令都是语句,x = 6 +(y = 5);在此语句中,子表达式中 y = 5是一个完整的指令,但是它只是一个语句的一部分。因为一条完全的指令不必是一个语句,所以分号被用来识别确实是语句的指令。赋值语句函数语句(function statement)引起函数的执行。副作用从C的角度来看,主要的目的是对表达式求值。给C一个表达式4+6,C将计算它的值为10.给C一个表达式state = 50;C将计算它的值为50。计算这个表达式的副作用就是把变量state的值改变为50.跟赋值运算符一样,增量运算符和减量运算符也有副作用,它们主要由于副作用而被使用。一个顺序点(sequence point)是程序执行中的一点;在该点处,所有的副作用都在进入下一步前被计算。在C中,语句里的分号标志了一个顺序点。它意味着在一个语句中赋值运算符、增量运算符及减量运算符所做的全部改变必须在程序进入下一个语句前发生。什么是完整表达式?一个完整的表达式(full expression)是这样一个表达式-它不是一个更大的表达式的子表达式。完整表达式的例子包括一个表达式语句里的表达式和一个while循环里作为判断条件的表达式。y = (4 + x+) + (6 + x+);表达式4 + x+不是一个完整表达式,所以C不能保证在计算子表达式4+x+后立即增加x。这里,完整表达式是整个赋值表达式,并且分号标记了顺序点,所以C能保证的是在程序进入后须语句前x将被增加两次。C没有指明x是在每个子表达式被计算后增加还是在整个表达式被计算后增加,这就是我们要避免使用这类语句的原因。复合语句复合语句(compound statement)是使用花括号组织起来的两个或更多的语句;它也被成为一个代码块(block)。总结:表达式和语句表达式是运算符和操作数的组合语句是对计算机的命令。有简单语句和复合语句,简单语句有,声明语句、赋值语句、函数调用语句、结构化语句、空语句。类型转换1、 当出现在表达式里时,有符号和无符号的char和short类型都将自动被转换为int,在需要的情况下,将自动被转换为unsigned int(如果short与int有相同的大小,那么unsigned short比int大;在那种情况下,将把unsigned short转换为unsigned int)。在K&R C下,但不是当前的C下,float将被自动转换为double。因为是转换成较大的类型,所以这些转换被称为提升(promotion)。2、 在包含两种数据类型的任何运算里,两个值都被转换成两种类型里较高的级别。3、 类型级别从高到低的顺序是long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int和int。一个可能的例外是当long和int具有相同大小时,此时unsigned int比long的级别更高。之所以short和char没有出现在此清单里,是因为它们已经被提升到int或也可能被提升到unsigned int。4、 在赋值语句里,计算的最后结果被转换成将要被赋予值的那个变量的类型。像规则1中一样,这个过程可能导致提升;但也可能导致降级(demotion),降级是将一个值转换成一个更低级的类型呢个。5、 当作为函数的参数被传递时,char和short会被转换成int,float会转换为double。可以通过函数原型来阻止自动提升的的发生。提升通常是一个平滑的无损害的过程,但是降级可能导致真正的问题。原因很简单:一个较低级别的类型可能不够大,不能存放一个完整的数。一个8字节的char变量可以存放101,但是不能存放整数22334.当把浮点类型降级为整数类型时,他们被趋零截尾或舍去。这意味着23.12和23.99都被截尾成23,-23.5被截尾成-23.指派运算符通常您应该避免自动类型转换,尤其是避免降级。圆括号和类型名一起构成了指派运算符(cast operator) (type)第六章C控制语句:循环=运算符是C的相等运算符(equality operator)while语句while(expression)statementwhile循环是使用入口条件的有条件循环当您确定需要循环时,应该使用哪一种呢?首先要确定您需要入口条件循环还是退出条件循环。通常是需要入口条件循环。有若干原因使得计算机科学家认为入口条件循环更好一些。首先是因为一般原则是在跳过(或循环)之前进行查看要比之后好;其次是如果在循环开始的地方进行循环判断,程序的可读性更强;最后一点是在很多应用中,如果一开始就不满足判断条件,那么跳过整个循环是最重要的。第七章 C控制语句:分支和跳转if语句被称为分支语句(branching statement)或选择语句(selection statement),因为它提供了一个交汇点,在此处程序需要选择两条分支中的一条前进。一般的形式如下:if (expression)statement;如果expression求得值为真(非0)就执行statement;否则跳过该语句;通常expression是一个关系表达式。也就是说,它比较两个量的大小。语句部分可以是一个简单语句,也可以是花括号标出的复合语句(代码块);即使,if中使用了一个复合语句,整个if结构仍将被看做一个简单语句。getchar()getchar()函数没有参数,它返回来自输入设备的下一个字符。ch = getchar();等同于scanf(%c,&ch);putchar()函数打印它的参数。putchar(ch);因为这两个函数仅仅处理字符,所以他们比更通用的scanf()和printf()更快而且更简洁。注意到它们不需要格式说明符,因为他们只对字符起作用。ctype.h系列字符函数如果程序之转换字符将所有的非字母字符(而不是空格符)保留下来,将会更好。cpyte.h的字符判断函数函数名为如下参数时,返回值为真isalnum()字母数字(字母或数字)isalpha()字母isblank()一个标准的空白字符(空格、水平制表符或者换行)或者任何其他本地化指定为空白符的字符iscntrl()控制符,例如ctrl+Bisdigit()阿拉伯数字isgraph()除空格符之外的所有可打印字符islower()小写字母isprint()可打印字符ispunct()标点符号(除空格和字母数字外的可打印字符)isspace()空白字符:空格、换行、走纸、回车,垂直制表符、水平制表符、或可能是其他本地化定义的字符isupper()大写字母isxdigit()十六进制数字字符ctype.h的字符映射函数tolower()如果参数是大写,返回相应的小写字符;否则,返回原始参数。toupper()如果参数是小写,返回相应的大写字符;否则,返回原始参数;注意:映射函数并不改变原始的参数,它们只返回改变后的值;else 与if配对如果没有花括号指明,else与和它最近的if相匹配花括号从技术角度讲,if else语句作为单个语句,所以不需要括上花括号。外部if也是单个语句,所以也不需要花括号。然而,当语句很长时,花括号使人更容易清楚发生了什么,并且当后来将其他语句加入if或循环中时它们提供了保护。C的逻辑运算符:&与、|或、!非改写拼写法:iso646.h头文件C99标准为逻辑运算符增加了可供选择的拼写法。它们在iso646.h头文件里定义。如果包含了这个头文件,就可以用and代替&,用or代替|,用not代替!。条件运算符?:C提供一种简写方式来表示if else语句的一种形式,这被称为条件表达式,并使用条件运算符(?:).x = (y 0)? y:y;expression1 ? expression2:expression3如果expression1为真,整个表达式的值为expression2,否则为expression3.通常,if else语句能完成与条件运算符同样的功能。但是,条件运算符语句更简洁;并且,依赖编译器,可以产生更精简的程序代码。循环辅助手段:continue和breakcontinue语句,该语句可以用于三种循环形式。当运行到该语句时,它将导致剩余的迭代部分被忽略,开始下一次迭代。如果continue语句处于嵌套结构中,那么它仅仅影响包含它的最里层的结构。注意,有两种方法可以避免使用continue,一种是省去continue,将循环的剩余部分放在一个else代码块中。continue的另一个用处是占位符。while(getchar () != n) ;孤立的分号难以被注意使用continue更具可读性。while(getchar () != n) continue;break语句循环中的break语句导致程序终止包含它的循环,并进行程序的下一阶段。如果break语句位于嵌套循环里,它只影响包含它的最里层的循环。和continue一样,当break使代更加复杂时不要使用它。break语句实际上是switch语句的附属物。break语句使程序直接转到该循环后的第一条语句去执行;在for循环中,与continue不同,控制段的更新部分也将被跳过。嵌套循环中的break语句只是使程序跳出里层的循环,要跳出外层的循环则还需要另外按一个break语句。多重选择:switch和break紧跟在switch后的圆括号里的表达式被求值,然后扫描标签(label)列表,直到搜索到一个与该值相匹配的标签。如果有被标记为default:的标签行,程序就跳到该行;否则,程序继续处理跟在switch语句之后的语句。break导致程序脱离switch语句,跳到switch之后的下一条语句。break语句用于循环和switch中,而continue仅用于循环。但是,如果switch语句位于一个循环中,则可以把continue用于switch语句的一部分。在这种情况下,就像在其他的循环中一样,continue会导致程序跳过该循环的其余部分,其中包括switch的其余部分。圆括号中的switch判断表达式应该具有整数值(包括char类型)。case标签必须是整型(包括char)常量或者整数常量表达式(仅包含整数常量的表达式)。不能用变量作为case标签。因而,switch结构是这样的switch (integer expression)case constant1:statementscase condtant2:statementsdefault:statements只取一行的首字符while (getchar () != n)continue;假设用户开始按下了回车键,以致遇到的第一个字符是换行符。下面的代码处理这种可能:if (ch = n)continue;switch和if else什么时候该使用swtich,而什么时候又该使用if else结构呢?通常是没有选择的。如果选择是基于求一个浮点型变量或表达式的值,就不能使用switch。如果变量必须落入某个范围,也不能很方便的使用switch。goto语句Kernighan和Ritchie认为goto语句非常容易被滥用,并且建议要谨慎使用,或者根本不用。goto语句包括两个部分:goto和一个标签名称。标签的命名遵循与命名变量相同的约定,如下例所示:goto part2;part2:printf (Refined analysis:n);避免goto4、跳到循环末尾并开始下一轮循环;用continue代替5、跳出循环:用break代替。实际上,break和continue是goto的特殊形式。使用它们的好处是它们的名字表明它们意味着什么;并且,因为他们不适用标签,所以不存在放错标签位置的潜在危险。6、胡乱的跳转到程序的不同部分:千万不要!但有一种goto的使用被许多C专业人员所容忍:再出现故障时从一组嵌套的循环中跳出(单条break仅仅跳出最里层的循环)第八章字符输入/输出和输入确认单字符I/O:getchar()和putchar()ANSI C将stdio.h头文件与使用getchar()和putchar()相关联缓冲区输入字符立即回显是非缓冲(unbuffered)或直接(direct)输入的一个实例,它表示您所键入的字符对正在等待的程序立即变为可用。相反,延迟会先是缓冲(buffered)输入的实例,这种情况下,您所见键入的字符被收集并存储在一个被称为缓冲区的(buffer)的临时存储区域。为什么需要缓冲区?首先,将若干个字符作为一个块传输比逐个发送这些字符耗费的时间少。其次,如果您输入有误,就可以使用您的键盘更正功能来修正错误。缓冲区分为两类:完全缓冲(full buffered)I/O和行缓冲(line-buffered)I/O。对完全缓冲输入来说,缓冲区满时被清空(内容被发送至其他目的地)。这种类型的缓冲通常出现在文件输入中。缓冲区的大小取决于系统,但512字节和4096字节是常见值。对行缓冲区I/O来说,遇到一个换行字符时将被清空缓冲区。键盘输入是标准的行缓冲,因此按下回车键将清空缓冲区。非缓冲输入函数getche()和用于不回显的非缓冲输入getch()对于getch,getche,getchar三者的认识,调用getch,getche,必须包含头文件conio.h,否则在用vc编译时提示找不到标识符getch,getche,getchar共同点:都是读入一个字符不同点:1、getch读入字符后不会将字符回显到屏幕,而getche、getchar二者有回显;2、getchar等待输入直到按回车才结束(即使输了N多字符,但没按回车,程序还是处于输入等待状态), 同时,回车前的所有输入字符都会逐个显示在屏幕上,但只有第一个字符作为函数的返回值;getch,getche,二者在用户输入一个字符后(包括 回车)就结束输入;下面是一个功能测试程序:#include #includeint main() char c; c=getch(); /*从键盘上读入一个字符不回显送给字符变量c*/ putchar(c); /*输出该字符*/ printf(nnext onen);c=getche(); /*从键盘上带回显的读入一个字符送给字符变量ch*/ putchar(ch);printf(下一个n);c=getchar();putchar(c);putchar(n);return 0;输出结果为:在C+中,若看输出结果,不能再使用C中的 getch() 函数,而是用 _getch() 代替.终止键盘输入文件、流和键盘输入文件(file)是一块存储信息的存储区域。从概念上说,C程序处理一个流而不是直接处理文件。流(stream)是一个理想化的数据流,实际输入或输出映射到这个数据流。键盘输入由一个被称为stdin的流表示,而到屏幕(或电传打字机、或其他输入设备)上的输出由一个被称为stdout的流表示。文件结尾检测文件结尾的一种方法是在文件中放置一个特殊字符来标志结尾。这是在例如CP/M、IBM-DOS和MS-DOS的文本文件中曾经使用的一种方法。现今,这些操作系统可以使用一个内嵌的CTRL+Z字符来标志文件结尾。这曾经是这些操作系统使用的唯一方法,但是现在还有其他的选择,例如根据文件的大小来判断文件的结束位置。所以现在的文本文件可能具有也可能没有内嵌CTRL+Z,如果该文件有,则操作系统会将该字符作为文件尾标记对待。第二种方法是让操作系统存储文件大小的信息。如果一个文件具有3000字节,而且程序已经读取了3000字节,则该程序就到达了文件尾。MS-DOS家族对二进制文件使用这种方法,因此方法允许文件拥有包括CTRL+Z在内的所有字符。DOS的较新版本对文本文件也使用这种方法。Unix对所有文件都是用此方法。对这两种方法,C的处理方法是让getchar()函数在到达文件结尾时返回一个特殊值,而不去管操作系统是如何检测文件结尾的。赋予该值的名称是EOF(End Of File,文件尾)。因此,检测到文件尾时getchar()的返回值是EOF。scanf()函数在检测到文件结尾时也返回EOF。通常EOF在stdio.h文件中定义,如下所示:#define EOF (-1)重定向和文件echo_eof words符号是Unix,Linux(和DOS)的重定向运算符。该运算符把word文件与stdin流关联起来(在Unix,Linux和DOS中,是另一个重定向运算符。该运算符会导致建立一个名为XXX的文件,要结束程序,请在一行的开始键入Ctrl+D(Unix中)或Ctrl+Z(DOS中)。组合重定向假设您希望制作文件mywords的一个副本,并将其命名为savewords。echo_eof savewords下面的命令同样可以实现这一功能,因为重定向运算符的顺序无关紧要:echo_eof savewords mywords简单的说,下面是在Unix、Linux或DOS下使用两个重定向运算符所遵循的规则:重定向运算符讲一个可执行(executable)程序(包括标准的操作系统命令)与一个数据文件连接起来。使用这些运算符时,输入不能来自一个以上的文件,输出也不能定向至一个以上的文件。除了偶尔在使用到一些对Unix shell,Linux shell或DOS具有特殊意义的字符时,名字和操作符之间的空格并不是必须的。例如,我们可以使用echo_eofbeets违反第一条规则addupcount违反第一条规则addupfishbeets fish违反第二条规则Unix和Linux和DOS还具有运算符,该运算符可使您向一个现有文件的末尾追加数据;还有管道运算符(|),它可以将一个程序的输出与第二个程序的输入连接起来。第九章 函数函数(function)是用于完成特定任务的程序代码的自包含单元。尽管C中的函数和其他语言中的函数、子程序或子过程等扮演着相同的角色,但是在细节上会有所不同。某些函数会导致执行某些动作。为什么使用函数?第一,函数的使用可以省去重复代码的编写。如果程序中需要多次使用某种特定的功能,那么只需编写一个合适的函数即可。程序可以在任何需要的地方 调用该函数,并且同一个函数可以在不同的程序中调用,就像在许多程序中需要使用putchar()函数一样。第二,即使某种功能在程序中只使用一次,将其以函数的形式实现也是有必要的,因为函数使得程序更加模块化,从而有利于程序的阅读、修改和完善。许多程序员喜欢把函数看做黑盒子,即对应一定的输入会产生特定的结果或返回某个数值,而黑盒的内部行为并不需要考虑,除非是该函数的编写者。编写函数代码之前首先需要考虑的时函数的功能以及函数和程序整体上的关系。定义带有参数的函数:形式参量void show_n_char (char ch,int num)这行代码通知编译器show_n_char 使用名为ch和num的两个参数,并且这两个参数的类型分别是char 和int。变量ch和num被称为形式参数(formal argument)或形式参量(formal parameter,现在这个名称更为正式)。如同函数内部定义的变量一样,形式参量是局部变量,它们是函数所私有的。这意味着可以在其它函数中使用相同的变量名。每当调用函数时,这些变量就会被赋值。ANSI C形式要求在每个变量前声明其类型。也就是说不能像通常的变量声明那样使用变量别表。void (int x,y,z) /不正确的函数头void (int x,int y,int z)/正确的函数头ANSI C也接受ANSI前的形式,但将其是为废弃不用的形式:void show_n_char (ch,num)char ch;int num;带参数函数的原型声明使用函数之前需要用ANSI原型声明该函数:void show_n_char(char ch,int num);当函数接受参数时,函数原型用过使用一个逗号分隔的类型列表指明参数的个数和类型。在函数原型中可以根据您自己的喜好省略变量名:void show_n_char (char ,int );在函数中使用变量名并没有实际的创建变量。这只是说明char代表一个char类型变量,以此类推。调用带有参数的函数:实际参数函数调用中,通过实际参数(actual argument)对ch和num赋值。show_n_char (SPACE,12);实际参数是空格字符和12,这两个数值被赋给show_n_char()中相应的形式参量:变量ch和num。换句话说,形式参量是被调函数中的变量,而实际参数是调用函数分配给被调函数变量的特定值。实际参数是函数调用时出现在圆括号中的表达式。而形式参量则是函数定义中在函数头部声明的变量。使用return从函数中返回一个值这种用来测试函数的程序有时被称作驱动程序。return返回值不仅可以被赋给一个变量,也可以被用做表达式的一部分。return的另一个作用是终止函数。函数声明只是将函数类型告诉编译器,而函数定义部分则是函数的实际实现代码。ANSI C的函数原型函数调用怎么实现的:调用函数首先把参数放在一个称为堆栈(stack)的临时存储区域,然后被调函数从堆栈中读取这些参数。但是这两个过程并没有互相协调进行。调用函数根据调用过程中的实际参数类型确定需要传递的数值类型,但是被调函数是根据其形式参数的类型进行数据读取的。int imax(int ,int);int imax(int a ,int b);第一种形式使用逗号对参数进行分隔;而第二种形式在类型后加入变量名。需要注意的是这些变量名只是虚设的名字,它们不必和函数定义中使用的变量名相匹配。无参数和不确定参数void print_name();这时有一个ANSI C编译器会假设您没有用函数原型声明函数,他就不会进行参数检查。因此为了表示一个函数确实不使用参数,需要在圆括号内加入void关键字。函数原型的优点函数原型是对语言的有力补充。他可以使编译器发现函数使用时可能出现的错误或疏漏。而这些问题如果不被发现的话,是很难跟踪调试出来的。金可以不使用函数原型,而使用旧函数声明形式(不说明参数的函数声明),但是这么做不仅没有任何优势,反而存在许多缺点。有一种方法,可以不使用函数原型却保留函数原型的优点。之所以使用函数原型,是为了在编译器变异第一个调用函数的语句之前向其表明函数的使用方法。因此可以在首次调用某函数之前对该函数进行完整的定义。这样函数定义部分就和函数原型有着相同的作用。通常对较小的函数会这样做:/下面既是一个函数的定义,也是它的原型int imax (int a,int b)return a b ? a:b;int main(void)z = imax (x ,50);递归C允许一个函数调用其本身。这种调用过程被称作递归(recursion)。递归有时很难处理,而有时却很方便实用。当一个函数调用自己时,如果编程中没有设定可以种植递归的条件检测,他会无限制地进行递归调用,所以需要进行谨慎处理。递归一般可以代替循环语句使用。有些情况下实用循环语句比较好,而有些时候实用递归更有效。递归方法虽然使程序结构优美,但其执行效率却没有循环语句高。递归的基本原理第1, 每一级的函数调用都有自己的变量。第2, 每一次函数调用都会有一次返回。当程序流执行到某一级递归的结尾时,它会转移到第一级递归继续执行。第3, 递归函数中,位于递归调用前的语句和各级被调函数具有相同的执行顺序。第4, 递归函数中,位于递归调用后的语句的执行顺序和各个被调函数的顺序相反。例如,打印语句#2位于递归调用语句之后,其执行顺序是:第4级、第3级、第2级和第1级。递归调用的这种特性在解决涉及反向顺序的编程问题时很有用。第5, 虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。函数代码是一系列的计算机指令,而函数调用就是从头执行这个指令集的一条命令。一个递归调用会使程序从头执行相应函数的指令集。除了为每次调用创建变量,递归调用非常类似于一个循环语句。实际上,递归有时可被用来代替循环,反之亦然。最后,递归函数中必须包含可以终止递归调用的语句。通常情况下,递归函数会使用一个if条件语句或其他类似的语句一遍当函数参数达到某个特定值时结束递归调用。尾递归最简单的递归形式是把递归语句放在函数结尾即恰在return语句之前。这种形式被称作尾递归(tail recursion)或结尾递归(end recursion),因为递归调用出现在函数尾部。由于递归尾的作用相当于一条循环语句,所以它是最简单的递归形式阶乘递归#includelongfact(intn);longrfact(intn);intmain(void)intnum;printf(Thisprogramcalculatesfactorals.n);printf(Enteravalueintherange0-12(qtoquit):n);while(scanf(%d,&num)=1)if(num12)printf(Keepinputunder13.n);elseprintf(loop:%dfactorial=%ldn,num,fact(num);printf(recursion:%dfactorial=%ldn,num,rfact(num);longfact(intn)longans;for(ans=1;n1;n-)ans*=n;returnans;longrfact(intn)longans;if(n0)ans=n*rfact(n-1);elseans-1;returnans;递归和反向计算十进制转换成二进制递归的优缺点使用递归既有优点也有缺点。其优点在于为某些编程问题提供了最简单的解决方法,而缺点是一些递归算法会很快耗尽计算机的内存资源。同时,使用递归的程序难于阅读和维护。所有C函数地位同等一个程序中的每个C函数和其他函数之间是平等关系。每一个函数都可以调用其他任何函数或被其他函数调用。main()函数是否与其他的函数不同?是的,函数main()是一个有点特殊的函数。因为在程序中当几个函数放在一起时,计算机将从main()中的第一个语句开始执行,但这也是其局限之处。同时main(void)也可以被其本身递归调用或被其他函数调用-尽管很少这么做。第10章 数组和指针数组:数组(array)由一系列类型相同的元素构成。可以使用声明来告诉编译器您需要一个数组。数组声明(array declaration)中包括数组元素的数目和元素的类型。float candy365;方括号()表示candy和其他两个标识符均为数组,方括号内的数字指明了数组所包含的元素数目。要访问数组中的元素,可以使用下
展开阅读全文