『C程序设计』读书笔记 关键字:c语言 原作者姓名:loose_went 文章原出处:vczx.com 写在前面: 《C程序设计》可以说是一本再基础不过的编程书了,但每读一遍的感觉却都是不同的,可以说,每读一遍,都会有很多新的收获。真所谓老书再读,回味无穷啊!此笔记是《C程序设计》谭浩强编著,清华大学出版社出版。除了将书中的重点知识点记下来外,也加入了我对知识点的理解,我想这一点是读书笔记的重要性所在。
第一章 概述 1. C语言的特点 ①语言简洁、紧凑,使用方便、灵活。共有32个关键字,9种控制语句。 ②运算符丰富,公有34种运算符。 ③数据结构丰富,数据类型有:整型、实型、字符型、数组、指针、结构体、共用体等。 ④具有结构化的控制语句(如if…else、while、do…while、switch、for) ⑤语法限制不太严格,程序设计自由度大。 ⑥允许直接访问物理地址,能进行位(bit)操作,可以直接对硬件操作。 ⑦生成目标代码质量高,程序执行效率高。 ⑧可移植性好。
第二章 数据类型、运算符与表达式
第三章 最简单的c程序设计
第四章 逻辑运算和判断选取控制
第五章 循环控制 1. 几种循环语句 goto语句(现已很少使用) while语句 先判断表达式后执行语句 do-while语句 先执行语句后判断表达式 for语句 2. Break语句和continue语句 Break语句用于跳出循环,continue用于结束本次循环。
第六章 数组 1. 一维数组 c规定只有静态存储(static)和外部存储(extern)数组才能初始化。给数组初始化时可以不指定数组长度。 2. 二维数组 3. 字符数组 部分字符串处理函数 puts(字符数组) 将一个字符串输出到终端。 gets(字符数组) 从终端输入一个字符串到字符数组,并且得到一个函数值,为该字符数组的首地址 strcat(字符数组1,字符数组2) 连接两个字符数组中的字符串,数组1必须足够大。 Strcpy(字符数组1,字符串2) 将字符串2拷贝到字符数组1中。 Strcmp(字符串1,字符串2) 比较字符串,相等返回0,字符串1>字符串2,返回正数,小于返回负数。 Strlen(字符数组) 求字符串长度。 Strlwr( 字符串) 将字符串中的大写字母转换成小写 Strupr( 字符串) 将字符串中的小写字母转换成大写 以上是一些比较常用的字符串处理函数。
第七章 函数 1. 关于形参和实参的说明 ① 在函数被调用之前,形参不占内存 ② 实参可以是常量、变量或表达式 ③ 必须指定形参的类型 ④ 实参与形参类型应一致 ⑤ 实参对形参的数据传递是"值传递",即单向传递 2. 函数返回值 如果想让函数返回一个值,在函数中就要用return语句来获得,在定义函数时也要对函数值指定类型,如果不指定,默认返回整型。 3. 函数调用 1)注意在函数调用时实参和形参的个数、类型应一一对应。对实参表求值的顺序是不确定的,有的系统按自左至右,有的系统则按自右至左的顺序。这一点要注意。 2)函数调用的方式:函数语句,函数表达式,函数参数 3)如果主调函数和被调函数在同一文件中,并且主调函数在前,那么一般要在主调函数中对被调函数进行说明。除非:(1)被调函数的返回值类型为整型或字符型(2)被调函数出现在主调函数之前。 4)对函数的说明和定义是不同的,定义是指对函数功能的确立,包括指定函数名,函数值类型,形参及其类型、函数体等。说明则只是对已定义的函数返回值类型进行说明,只包括函数名、函数类型以及一个空的括弧,不包括形参和函数体。 5)c语言允许函数的递归调用(在调用一个函数的过程中又出现直接或间接的调用该函数本身)。 4. 数组作为函数参数 1)数组元素作为函数参数 和一般变量相同 2)数组名作参数应该在主调和被调函数分别定义数组,形参数组的大小可以不定义。注意:数组名作参数,不是单向传递。 3)多维数组作参数,在被调函数中对形参数组定义时可以省略第一维的大小说明,但不能省略第二维或更高维的说明。 5. 局部变量和全局变量 从变量作用域角度分,变量可分为局部变量和全局变量。 1)内部变量(局部变量) 在一个函数内定义,只在函数范围内有效的变量。 2)外部变量(全局变量) 在函数外定义,可以为本文件其它函数所共用,有效范围从定义变量的位置开始 到本文件结束。建议尽量少使用全局变量,因为它在程序全部执行过程中都占用 资源,而且使函数的通用性降低了。如果在定义外部变量之前的函数要想使用该 外部变量,则应在该函数中用extern作外部变量说明。 6. 动态存储变量与静态存储变量 从变量值存在的时间(生存期)角度来分,可分为静态存储变量和动态存储变量。静态存储指在程序运行期间给变量分配固定的存储空间,动态存储指程序运行期间根据需要动态的给变量分配存储空间。 C语言中,变量的存储方法分为两大类:静态存储类和动态存储类,具体包括:自动的(auto),静态的(static),寄存器的(register),外部的(extern)。 1) 局部变量的存储方式 函数中的局部变量如不作专门说明,都之auto的,即动态存储的,auto可以省略。局部变量也可以定义为static的,这时它在函数内值是不变的。静态局部变量如不赋初值,编译时系统自动赋值为0,动态局部变量如不赋初值,则它的值是个不确定的值。C规定,只有在定义全局变量和局部静态变量时才能对数组赋初值。为提高执行效率,c允许将局部变量值放在寄存器中,这种变量叫register变量,要用register说明。但只有局部动态变量和形式参数可以作为register变量,其它不行。 2) 全局变量的存储方式 全局变量在函数外部定义,编译时分配在静态存储区,可以在程序中各个函数所引用。多个文件的情况如何引用全局变量呢?假如在一个文件定义全局变量,在别的文件引用,就要在此文件中用extern对全局变量说明,但如果全局变量定义时用static的话,此全局变量就只能在本文件中引用了,而不能被其它文件引用。 3) 存储类别小结 从作用域角度分,有局部变量和全局变量 局部变量:自动变量,即动态局部变量(离开函数,值就消失) 静态局部变量(离开函数,值仍保留) 寄存器变量(离开函数,值就消失) (形参可定义为自动变量和寄存器变量) 全局变量:静态全局变量(只限本文件引用) 全局变量(允许其它文件引用) 从存在的时间分,有静态存储和动态存储 动态存储:自动变量(本函数内有效) 寄存器变量(本函数内有效) 形参 静态存储:静态局部变量(函数内有效) 静态全局变量(本文件内有效) 全局变量(其它文件可引用) 从变量值存放的位置分 静态存储区:静态局部变量 静态全局变量 全局变量 动态存储区:自动变量和形参 寄存器内:寄存器变量 7. 内部函数和外部函数 内部函数:只能被本文件中的其它函数调用,定义时前加static,内部函数又称静态函数。 外部函数:可以被其它文件调用,定义时前加extern,如果省略,则隐含为外部函数,在需要调用此函数的文件中,一般要用extern说明。
第八章 预编译处理
第九章 指针 指针说白了就是地址。指针变量就是用来存放指针(地址)的变量。 4. 函数的指针和指向函数的指针变量
指针应该说是c语言中比较重要的概念,也是c语言的精华,它有很多优点,但用不好也会带来严重性的错误,这就需要我们多用,多练,慢慢的积累经验。
第十章 结构体与共用体 1. 定义 结构体定义的一般形式: struct 结构体名{ 成员列表 }; 定义一个结构体变量可以这样定义:struct 结构体名 结构体变量名; 2. 结构体变量的引用 在引用结构体变量时应注意以下规则: 1)不能将结构体变量作为一个整体输入输出,只能对变量当中的各个成员输入输出。新标准C允许将一个结构体变量直接赋值给另一个具有相同结构的结构体变量。 3. 结构体变量的初始化 如: struct student {long int num; char name[20]; char sex; char addr[20]; }a={89031,"Li Lin",'M',"123 Beijing Road" }; 4. 结构体数组 struct student stu[4]; 定义了一个数组stu,其元素为struct student类型,数组有4个元素。注意数组各元素在内存中是连续存放的。 在定义结构体数组时,数组元素个数可以不指定。编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。 5. 指向结构体变量的指针 因为结构体变量在内存中是连续存放各成员的,因此我们可以将结构体变量在内存中的起始地址存放到一个变量中,那么这个变量就是指向结构体变量的指针。 注意将结构体变量的首地址赋给指针变量的形式: struct student stu_1; struct student *p; p=&stu_1; //要加取地址符 而指向函数和指向字符串的指针不用 在对引用结构体变量中的成员时,有三种方式: 以上面的结构体为例:设p为指向此结构体变量的指针,即p=&a; 1) a.num 2) (*p).num 3) p->num 6. 指向结构体数组的指针 struct student *p; struct student stu[4]; p=stu; 则p为指向结构体数组的指针变量。这里应注意p++,p指向stu[0],p++则指向stu[1]。P指向的是数组中一个元素的首地址,而不能让p指向元素中的某一成员,如p=&stu[I].name是不对的。 7. 用指向结构体的指针作函数参数 虽然ANSI C允许用整个结构体作为函数参数,但要将全部成员值一个一个传递,开销大。所以用指针作参数,能提高运行效率。 Struct student stu; 用整个结构体作为参数调用形式: fun( stu ); 而且被调函数fun中也要定义成结构体变量,struct student stu; 用指针作参数调用形式: fun( &stu ); 被调函数fun中定义成指针变量,struct student *p; 8. 用指针处理链表 链表是一种重要的数据结构,原因就在于它可以动态的进行存储分配。链表都有一个头指针,用来存放整个链表的首地址。链表的定义形式如下: struct node{ int num; … struct node *next; }; next用来存放下一节点的地址。 如何进行动态的开辟和释放存储单元呢?c提供了以下有关函数: 1) malloc(size) 在内存的动态存储区开辟一个长度为size的连续空间。成功返回空间首地址,失败返回0; 2) calloc(n,size) 在内存的动态存储区开辟n个长度为size的连续空间。成功返回空间首地址,失败返回0; 3) free(ptr) 释放由ptr指向的内存区。Ptr是最近调用一次调用malloc和calloc时返回的值。 上面函数中,n和size为整型,ptr为字符指针。 9. 共用体 定义形式: union 共用体名 { 成员列表 }变量列表; 共用体和结构体类似,只是有一点不同,结构体中个成员的起始地址不同,结构体变量在内存中的长度为各成员长度之和;而共用体中个成员的起始地址相同,共用体变量所占的内存长度为最长的成员的长度。 共用体类型数据的特点: 1) 同一个内存段可以存放几种不同类型的成员 2) 共用体变量中起作用的成员是最后一次存放的成员 3) 不能对共用体变量名赋值,不能在定义时初始化。 4) 不能把共用体变量作为函数参数 5) 共用体类型可以出现在结构体定义中,反之也可,也可以定义共用体数组。 另外,结构体名可以作为参数,而共用体名不可以。 10. 枚举类型
第十一章 位运算 1)概述 所谓位运算是指进行二进制位的运算。在系统软件中,常要处理二进制位的问题。 c提供的位运算符有: & 按位与 | 按位或 ^ 按位异或 ~ 取反 << 左移 >> 右移 &对于将一个单元清零、取一个数中的某些指定位以及保留指定位有很大用途。 |常被用来将一个数的某些位置1。 ^判断两个位值,不同为1,相同为0。常用来使特定位翻转等。 ~常用来配合其它位运算符使用的,常用来设置屏蔽字。 <<将一个数的各二进制位全部左移,高位左移后溢出,舍弃不起作用。左移一位相当于该数乘2,左移n位相当于乘2n。左移比乘法运算要快的多。 >>右移时,要注意符号问题。对无符号数,右移时左边高位移入0,对于有符号数,如果原来符号位为0(正数),则左边移入0;如果符号位为1(负数),则左边移入0还是1要取决于系统。移入0的称为"逻辑右移",移入1的称为"算数右移"。 2)位段 将一个字节分为几段来存放几个信息。所谓位段是以位为单位定义长度的结构体类型中的成员。如: struct packed-data{ unsigned a:2; unsigned b:6; unsigned c:4; unsigned d:4; int I; }data; 其中a,b,c,d分别占2位,6位,4位,4位。I为整型,占4 个字节。 对于位段成员的引用如下: data.a = 2;等,但要注意赋值时,不要超出位段定义的范围。如位段成员a定义为2位,最大值为3,即(11)2,所以data.a=5;就会取5的两个低位进行赋值,就得不到想要的值了。 关于位段的定义和引用,有几点重要说明: ①若某一个段要从另一个字开始存放,可以定义: unsigned a:1; unsigned b:2; unsigned :0; unsigned c:3; (另一单元) 使用长度为0的位段,作用就是使下一个位段从下一个存储单元开始存放。 ②一个位段必须存放在用一个存储单元中,不能跨两个单元。 ③可以定义无名位段。如: unsigned a:1; unsigned :2; (这两位空间不用) unsigned b:3; ④位段的长度不能大于存储单元的长度,也不能定义位段数组。
第十二章 文件 1) 概述 c语言将文件看成一个字符的序列,分为ASCII文件(文本文件)和二进制文件。即一个c文件就是一个字节流或二进制流。 ASCII文件每一个字节放一个ASCII码,代表一个字符,输出与字符一一对应,便于逐个处理字符,但占用空间较多。二进制文件按内存中的存储形式原样输出到磁盘上,节省空间,由于输出与字符不对应,不能直接输出字符形式,一般用于保存中间结果。目前c对文件的处理只有缓冲文件系统一种方法,即无论是从程序到磁盘文件还是从磁盘文件到程序,数据都要先经过缓冲区,待缓冲区充满后,才集中发送。 2) 文件夹类型指针 在缓冲文件系统中,关键的概念是文件指针。因为每个被使用的文件都在内存中开辟一个缓冲区,来存放文件有关信息。这些信息保存在一个结构体变量中,该结构体类型是由系统定义的,取名为FILE,在stdio.h中定义。 FILE *fp; 定义了一个文件指针变量fp,以后对文件的操作都是通过fp进行的。 3) 文件的打开与关闭 在对文件读写之前,要先打开文件。 打开文件的函数为:fopen(),调用方式为: FILE *fp; fp=fopen( filename,使用文件方式 ); fopen()失败返回一个空指针NULL,成功则返回一个指向"filename"的文件指针,赋给fp,这样fp就和打开的文件联系在一起了。或者说,fp指向了"filename"。 文件使用方式:r,w,a,rb,wb,ab,r+,w+,a+,rb+,wb+,ab+,具体含义要记住。 4)文件的关闭 为了防止数据丢失,程序结束前,务必将打开的文件关闭,即将文件指针与文件脱钩。用fclose(文件指针)函数关闭文件,执行函数后,先将缓冲区中的数据送到磁盘文件,然后释放文件指针。成功返回0,失败返回非0。 5)文件的读写 文件打开后,就可以对其读写了,常用的文件读写函数有: ①fputc和fgetc fputc将一个字符写到文件,形式为fputc( ch, fp );将字符ch写入fp所指向的文件。成功返回该字符,失败返回EOF,EOF在stdio.h中定义为符号常量-1。 fgetc从指定文件读入一个字符,该文件必须是以读或读写方式打开的。调用形式为ch=fgetc(fp);从fp指向的文件读入一个字符赋给ch,当文件结束时,fgetc返回一个EOF,我们可以用函数feof(fp)来判断是否已到文件尾,返回1表示已到文件尾,否则返回0。这个函数适用于文本文件和二进制文件。 ②fread和fwrite函数 可以读写一组数据。调用形式如下: fread( buffer, size, count, fp ); fwrite( buffer, size, count, fp ); buffer为一个指针,对fread来讲,是指从文件读出数据的存放地址,对fwrite来讲,是要写入文件的数据的地址。 size 要读写的字节数 count 要进行读写多少个size字节的数据项(书上这么说)其实就是读写的次数 fp 文件指针 这两个函数返回值成功为1,失败为非1,一般用于二进制文件的读写。 注意:有些c编译系统不具备这两个函数。 ③fprintf()和fscanf()函数 格式化输出和输入函数,与printf()和scanf()作用相似,只有一点不同,fprintf()和fscanf()的读写对象不是终端而是磁盘文件。调用方式: fprintf(文件指针,格式字符串,输出列表); fscanf(文件指针,格式字符串,输出列表); ④fgets()和fputs()函数 作用是读写一个字符串,如: fgets(str,n,fp); 意为从fp指向的文件读出n-1个字符,存放到str中,成功返回str的首地址。 fputs( "China", fp ); 把字符串China写入fp指向的文件。成功返回0,失败为非0。 6)文件的定位 文件中有一个位置指针,指向当前读写的位置,如果要强制改变位置指针的位置,可以用有关函数: ①rewind 使位置指针重新返回文件的开头 ②fseek() fseek()函数可以任意改变位置指针的位置,以实现随机读写文件。调用形式: fseek( 文件指针类型,位移量,起始点 ); 起始点有以下三个值: SEEK_SET或0 文件开始 SEEK_CUR或1 文件当前位置 SEEK_END或2 文件末尾 位移量指以起始点为基点,移动的字节数(正数向文件尾移动,负数向文件头移动),一般位移量用long型数据,以避免大于64K的文件出错。Fseek()函数一般用于二进制文件,因为文本文件要进行字符转换,计算时会发生混乱。 Fseek( fp, 100L, 0 ); 将位置指针从文件头向文件尾移动100个字节处。 Fseek( fp, 50L, 1 ); 将指针从当前位置向文件尾移动50个字节处。 Fseek( fp, -10L, 2 ); 将指针从文件尾向文件头移动10个字节处。 ③ftell() 得到流式文件位置指针的当前位置,成功返回相对于文件头的位移量,失败返回-1L。
另外,由于ANSI C不使用非缓冲文件系统,而其它C系统还用到非缓冲文件系统,所以对于这一章节只是略微的看了一下,不至于以后见到这样的程序不认识,呵呵。这一节主要讲了几个文件读写的有关函数,看了也没做笔记。如果关心的话,自己看一下吧。 至此,这本语言类的基础书又温习了一遍,由于工作太忙,花了我半个月的时间。不过总的来说,收获还是很大的,有很多以前没有发现的新东西,也有很多以前理解较浅显的东西,这次加深了理解。其实,看完了一章后,最好将书后的习题一一解答,因为这是对这章知识点的考查。同时,动手编一下小程序,也能提高自己的编程能力。 c编译系统在对程序进行通常的编译之前,先进行预处理。c提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译
|