




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
the c programming language读书笔记更新时间:2011-08-21 14:01:08来源:工业360核心提示:the c programming language读书笔记写在前面的话作为计算机专业的学生来说,学习编程是一件不可避免的事情,然而,想成为真正的程序员(相信这是我们共同的理想),光凭我们在课堂里学到的那些东西是远远不够的,我们必须读大量的书,写大量的代码,经历一个艰难而快乐的过程之后,才能真正走入这个领域。因此利用好这个漫长的暑假,对于一个想学好编程的人来说是很有意义的。为什么要学习c语言?对于初学者来说,往往第一个要学的就是c语言。为什么要学习c语言?对于今天这个惟利是图的世界来说,恐怕初学者第一要问的就是这个问题,他们中的很多人都会说在拥有c+,java,c这些高级语言的今天,c能做什么呢?在网络中,得到的回答往往是:“c无所不能“,然后是一些语重心长的说教,呵呵,对于一个对编程知道不多的人来说这样的回答没有任何意义,因此我对这样的问题的回答是:那些高级语言的出现并不能结束c长达三十余年的长盛不衰,c语言没有被任何一种语言所代替。相反,有些和c同时代的那些东西,恐怕今天的人连名字都忘了。在这个世界的每个角落都有无数的编程爱好者和从业人士对c有着无与伦比的狂热。这是事实,这是真理,它证明了一切。至于这个原因何在?c魅力到底在那呢?呵呵,这正是我们要在学习中必须弄明白的事情之一。现在就请你记住这个问题。选择什么书?选好了学习对象,那么最关键的无疑是选择一本好书,毫无疑问没有人能比K&R对c更有发言权了,用他们亲笔所写的The C Programming Language来入门是再合适不过了,尽管现在市场上关于c的教材到处都是,但是大半都是以这本书为根基的,严格来说关于c的一切疑问都可以在这本书中得到解释。我的这个系列笔记也是以此为基础,另外会引入另一本巨作C专家编程的观点(强烈向有一定c基础的朋友推荐此书)。当然,这本书写于1994年,对于c99的东西缺乏说明,但是,请记住,你要学的是那些万变不离其宗的东西,没有基础就追求那些时尚是很不合适的,也是成功率很低的事情。好了,下面就让我们开始这个暑假的编程学习之旅吧。目录写在前面的话 1为什么要学习c语言? 1选择什么书? 1目录 2第一章 认识C语言 3关于EOF 3声明和定义 3字符数组和字符串 4第二章 走过类型和表达式的迷宫 5变量和常量 5关于换码序列 6关于+运算符 7第三章 当好机器的老板 8尽量使用局部变量 8注意和正视一些看起来像bug的语言特性 9第四章 关于程序风格的一点讨论。 11合理利用空白字符,使得代码风格简洁清楚 11尽量分解问题,多写函数 13尽量使用有意义的名字,少用无意义的符号和幻数 14注释 15第五章 程序设计思想初步 16第一步,建立一个项目: 16第二步,确定要提供的接口 16第三步,实现接口函数 17第四步 连接各个接口 23第六章 尽量利用能利用的资源 24第一章 认识C语言当然,我应该是第四次读这本书了,每次重读都收获不少。都引起我新的思考,很难想象他的魔力何在。本章的内容是很容易理解的概念,对于编程学习者是最起码的知识,但是有些东西还是被初学者忽视了,以至于它们在各个技术社区的初学者问题中层出不断。其实,这些东西在第一章就得到了比较完整的解释,下面只是把他们提出来强调一下。关于EOF在很多书籍中都存在这样的循环语句:while ( getchar() != EOF ) .很多人不明白这个EOF为何物?具体如何操作?以至于让上面的语句变成了无限循环。呵呵,其实EOF是文件结束符(end of file),在第七章有说明,其为系统常量。大多数情况下,值为-1,当然你在终端输入-1,循环并未结束,why?how to do?恩,你在c专家编程里会了解到,c的第一批使用者都是系统设计者和编译器设计者。在他们的理念里,信息往往以文件为单位的。这个标志只是文件结束的状态,一般不由用户提供。键盘等输入端在os中是个极其特殊的文件,需要用户显式标志文件结束。说是系统常量,当然有着系统依赖性(因此说EOF的值就是-1,也是不正确的),因此不同的系统标志方式就完全不同。Windows下是ctrl+z。linux下是ctrl+D.,这是从系统的角度来说。另外更直接的原因是,为了保证程序的完整性,EOF应该排除在可输入字符之外,这是从语言的根源上来考虑的。声明和定义尽管这个问题地球人都知道,但是真正说的清楚的人并不多。有人认为变量的声明和定义没有什么区别。有人认为这个与初始化有关系(我曾经就这样白痴,汗),对于后者那就是根本就不明白这两个概念。声明只是给编译器一个提示,有这么个名字存在于程序中,和运行环境毫无关系,可以重复出现。定义是具体分配内存空间和指定了变量的位子(左值),在同一域中只能出现一次。对于前者的观点,在单文件程序中几乎找不到错误。但是,你把这样的句子放入头文件int a,一旦这个头文件被重复包涵,必然出现链接错误,其实这样:extern int a;/声明int a;/定义字符数组和字符串有人认为这两个是同一概念,是这样吗?不,完全不是,前者为容器(数据结构),后者为数据。这样说也许太理论化,好,我们来改写下那个hello world#include stdio.h#include stdlib.hint main() printf( post.contenthello world ); system(pause); return 0;呵呵,什么也没有?是的。还记得字符串是怎么结束的吗?post.content post.contenthello world 是个常量数组。但是字符串却是“”。这章尽管非常简单,但是每个例子都经典之作,建议你每个都抄一遍,你能从代码中学到更多的东西。第二章 走过类型和表达式的迷宫这本书的第二章的内容是学习编程中最基础东西,任何一门语言都会告诉你他支持那些数据类型、那些运算、有那些特点、以及有那些不完善的东西。学习这些东西相对来说是单调了点,麻烦了点,但是只有通过了这座迷宫,你才能进入C这个神奇的世界。因此初学者的成功至少有一半来自“耐心”。呵呵,准备好了吗?这章的内容还是非常简单的,但是作者的字里行间隐藏了很多重要的信息,不加注意就会从我们的眼皮低下溜了去,下面将一一列出以示强调。变量和常量很多人对于他们的区别很模糊,个人认为他们的主要区别在于是否分配内存空间,换句话说,就是是否存在左值。左值是什么?在第二章的从头到尾好像没找到这个名词,呵呵,你可以在附录中关于变量的条目中找到他,其实就是变量的地址,变量一旦被定义,左值就被确定了,一直到他的生存期结束。我们通常说的变量的值是指变量的右值。这才是我们能操作的对象。根据这个理论,那么就不难知道其实被const修饰的对象不是常量,他有左值,但是这里有个小麻烦,在本章的开头写明了被const修饰的是常量(第二章章第二段有个()说明),我查看了原版,并没有这个补充说明,看来应该是译者的理解,在C专家编程中的一个例子证明了我的想法是正确的,例子如下:#include “stdio.h”#include “stdlib.h”#define one 1const int two = 2;int main() int ix = 1; switch( ix ) case one: printf( this is 1 );/*ok*/ break; case two: printf( this is 2 );/*error*/ system( pause );return 0;大家都知道,case后面只能跟常量表达式,因此被const修饰的变量不是常量,只是变量的右值一般不能改变罢了。另外你也可以从上面感觉出#define和const的区别。关于换码序列这个更多地方叫转义字符,他们大多数是有一些特殊的功能的字符,在上篇笔记中你已经看到了他的一点威力,下面我们再看一段代码:#include stdio.h#include string.h#include stdlib.hint main() int ix; ix = strlen( post.contentabc ); printf( this is %dn, ix ); ix = strlen( post.content7abc ); printf( this is %dn, ix ); system( pause );return 0;你会发现,两个差不多的字符串长度完全不一样,什么回事呢?第一个我们可以理解:post.content是字符串结束符,因此其后的任何东西都不能算字符串的内容,因此长度为0。但是第二个呢?我们查了换码序列表就知道post.content7这个为一个字符,因此长度为4。这个时候问题来了,编译器为什么没把post.content7理解为post.content07呢?如果这样的话长度也将为0,我们又没人为的加分割符号,呵呵,显然这个和编译器的具体实现相关,凭我们现有知识无法弄明白这点,姑且留着,等待“悟“的一天吧,相信我,这绝对是一种享受。关于+运算符在很多教材上都有个看起来很经典的题目,其代码如下:#include stdio.h#include stdlib.hint main() int ix, iy; iy = 1; ix = ( iy+ ) + ( iy+ ) + ( iy+ ); printf( this is %dn, ix ); iy = 1; ix = ( +iy ) + ( iy+ ) + ( iy+ ); printf( this is %dn, ix ); iy = 1; ix = ( +iy ) + ( +iy ) + ( iy+ ) ; printf( this is %dn, ix ); iy = 1; ix = ( +iy ) + ( +iy ) + ( +iy ) ; printf( this is %dn, ix ); system( pause );return 0;呵呵,是不是很晕?这个本来无非为了说明先加后加的问题,这个地球人都知道,这里不加说明了,但是这样的程序本身就有很大的问题,编译器的运算并非一定是从左到右的(有些是按树的遍历来算的),因此你会发现不同的编译器结果会不一样,关于这个第二章的结尾有很完整的解释,我就不再多说了,总之,这个测试本身就违背了语言的特性。第三章 当好机器的老板无论什么时候我们都不该忘记我们是在学一门语言,而学语言的基本要求是:准确无误的用它来表示自己的意图,不仅要让机器读懂,也要让别人(只要他会c语言)读懂你的意思。记住,语言是用来交流的,不论是编程语言还是自然语言。现在让我们对这两个交流的对象分别作个分析,如何才能让他们明白你想干什么,打算怎么干。对于机器来说,我们要做的相对要简单点,编程语言的语法比自然语言要简单的多了,一切都由顺序、选择、循环三种结构复合而成,初学者要做的只是走一个“抄写改写模仿习惯”的过程而已。等这些语句成了你的习惯那就太好了,就像你说汉语的时候不会去考虑你用的是陈述句还是感叹句,呵呵,(这个让我想起了我糟糕的英语,汗)。当然我们对机器要做的远远不止这些,让机器读懂这只是第一步而已,如何让机器按照我们的意思运行的更好、更快才是我们要追求的境界,当然,这个境界没有止境。得在经验中慢慢积累,下面只是提出几个个人的建议而已:尽量使用局部变量因为c语言有个特点,在同个域中的变量必须定义在所有处理语句之前(分程序 除外),这意味着在程序开始的时候就必须分配好所有的静态空间,而很多数据在程序中用很少,因此我们需要减少这些不必要的开销,灵活运用分程序可以将这些对象进一步局部化,比较下面两段代码:Code1: #include stdio.h#include stdlib.hint main() int ix; char c; scanf( %c, &c ); if( c = y) ix = 100; printf( this is %d! n, ix ); system( pause );return 0;Code2#include stdio.h#include stdlib.hint main() char c; scanf( %c, &c ); if( c = y) int ix = 100; printf( this is %d! n, ix ); system( pause );return 0;你会发现如果我们不输入y系统就没有必要为ix分配空间。注意和正视一些看起来像bug的语言特性比如switch语句,可以说从c语言建立的那天起对他的争论就没有停止过,它的向下穿越给我们带来了不少麻烦。以至于在ISBN7-115-10627-4/ISBN的第二章中把它说成是“多做之过“,但是我们发现有时候它的功能还是不可代替的,比如判断一个数是否属于某个离散集合:#include stdio.h#include stdlib.hint main() int i; while( scanf( %d, &i ) != EOF ) switch( i ) case 1: case 2: case 3: case 5: case 8: case 13: printf( yes!n ); break; default : printf( no!n ); break; system( pause );return 0;呵呵,这个数列大家都熟悉,但是除了switch语句你能找到比他更简洁的表示方法吗?但这正是运用了语句的向下穿越性啊。goto语句也有类似的情况,只要我们仔细研究,这些看起来很麻烦的东西都会变得非常美好。好了,对机器的交流我们就说到这儿吧。在下次笔记中我们将谈谈和人的交流程序的风格问题。第四章 关于程序风格的一点讨论。到目前为止,我们已经了解c程序的基本元素,在进入过程化程序设计之前,我个人认为该对编码习惯做个良好的开端。关于程序设计风格问题,严格来说是一个没答案的讨论,随着编码经验的增加,我们在不同的阶段会有不同的认识,不同的出发点,本文要说的也只是笔者了两年来的编码体会,写出来的确需要勇气(毕竟在很多人眼里我没有这个资格),只是讨论的目的,绝无要误人子弟之意。一个好的程序员写出来的代码不仅要让机器运行起来并实现想得到结果,而且要让任何一位懂c语言的人看了就明白这段代码是什么意思,而不是需要大量的注释和文档来帮助他搞懂你的意思(当然作为辅助还是必须的)。以下就是个人的一些体会:合理利用空白字符,使得代码风格简洁清楚下面这段代码,实在让人看起来费劲;#include “stdio.h”#include “stdlib.h”int main()int a21,i,j;for(i=0;i2;+i)for(j=0;j1;+j)scanf(%d,&aj);for(i=0;i2;+i)for(j=0;j1;+j)printf(%d,aj);system(pause);return 0;当然,习惯在IDE下编写代码的朋友们可以借助环境的自动缩进(在非windows环境下往往就没这么舒服,得自己手动缩进),让上面的东西稍微好一点:#include “stdio.h”#include “stdlib.h”int main() int a21,i,j; for(i=0;i2;+i) for(j=0;j1;+j)scanf(%d,&aj); for(i=0;i2;+i) for)j=0;j1;+j) printf(%d,aj); system(pause); return 0;但是,看上去还是有点乱,特别是表达式的写法,如果你是telnet环境下的bbs里看这段代码对于眼睛来说不能不说是场恶梦。于是我们在运算符和括号的里恻各加个空格。并将不同意义的变量分开定义,哪怕他们属于同一类型。#include “stdio.h”#include “stdlib.h”int main() int a 2 1 ; int i, j; for( i = 0; i 2; +i ) for( j = 0; j 1; +j )scanf( %d, &a i j ); for( i = 0; i 2; +i ) for( j = 0; j 1; +j ) printf( %d, a i j ); system( pause ); return 0;呵呵。别看只是几个空格的问题。当你的代码很长的时候它可以帮助你让别人有耐心读下去,如果一眼看去符号字母集成一堆,谁还有兴趣看呢?尽量分解问题,多写函数简化main函数。比如上面那段代码可以写成下面这样,你会觉得更容易理解代码的功能、#include “stdio.h”#include “stdlib.h”const int* scan( int *p ) int i, j; for( i = 0; i 2; +i ) for( j = 0; j 1; +j )scanf( %d, ( p + j ) + i * 2 ); return p;void print( const int *p )int i, j;for( i = 0; i 2; +i ) for( j = 0; j 1; +j ) printf( %d, *( ( p + j ) + i * 2 ) );int main() int a 2 1 ; print( scan( &a 0 0 ) ); system( pause ); return 0;当然。对于实现细节来说,是更复杂化了。但是我们看一眼main函数就知道这是在干什么.。而对于我们的用户来说需要知道的往往只是接口,函数的细节通常被隐藏起来。因此上面的代码写在同一文件中是不合适的。这个关系到设计模式的问题。这里不再多说了。朋友们可以去看看设计模式这本书。尽量使用有意义的名字,少用无意义的符号和幻数在上面的例子中函数的名字使得我们对它的功能一目了然,但是这段代码还是有些麻烦,大家都知道数组越界是个要不得的错误,因此我们用循环测它。但是2这个数字意思非常不明白,时间一长。程序一复杂我们就会搞不明白这个2是干嘛的,因此我们得用宏替代它,经过修改,把这些代码写成单文件如下所示(再次说明,单文件不是好的选择,这里只是为了显示方便):#include “stdio.h”#include “stdlib.h”#define SIZE_1 2#define SIZE_2 1const int* fun1( int *p ) int i, j; for( i = 0; i SIZE_1; +i ) for( j = 0; j SIZE_2; +j )scanf( %d, ( p + j ) + i * 2 ); return p;void fun2( const int *p )int i, j;for( i = 0; i SIZE_1; +i ) for( j = 0; j ptr = 0; p-size = 0; return p;status list_destroy( list *pev ) if( pev = 0 ) return ERR; delete_all( pev ); free( pev ); return OK;按理说,函数不能返回指针,呵呵,这里有个很多初学者都误会的问题,返回局部对象的左值和局部对象的引用(后者是c+中的说法)被返回的确不可以,因为局部对象在函数的活动记录(即函数调用栈中)分配,函数一旦结束局部对象被回收,返回的将是无效地址。因此象下面这样的函数是错误的,int* f()int *p, a; p = &a; return p;但是由malloc分配的是堆上分配的,他不会随着函数的结束而被回收。但是这样用要相当小心,必须防止内存泄漏。程序结束前必须free掉该空间。下面就是完整的list.c#include stdio.h#include stdlib.h/*严格来说此上面两处该用尖括号,由于网页显示不得已为之*/#include list.hlist* list_init ( void ) list *p = ( list* )malloc( sizeof( list ) ); if( p = 0 ) return 0; p-ptr = 0; p-size = 0; return p;status delete_all( list *pev ) int ix; if( pev = 0 ) return ERR; if( pev-size = 0 ) return ERR; for( ix = 0; ix size; +ix, +pev-ptr ) delete_node( pev, pev-ptr ); return OK;status insert_node( list *p, const type date ) list_node *pev = p-ptr; ; if( p = 0 ) return ERR; pev = find_node( p, date ); if( pev = 0 ) type ia; printf( 输入要插入的数n ); scanf( %d, &ia ); add_node( p, ia ); else type ia; list_node *pv = ( list_node* )malloc( sizeof( list_node ) ); if( pev = 0 ) return ERR; printf( 输入要插入的数n ); scanf( %d, &ia ); pv-date = ia; pv-next = pev-next; pev-next = pv; p-size+; return OK;status list_print( const list *pev ) int ix; list_node *p = pev-ptr; if( pev = 0 ) r
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 【正版授权】 ISO 15614-11:2025 EN Specification and qualification of welding procedures for metallic materials - Welding procedure test - Part 11: Electron and laser beam welding
- 2025华电内蒙古腾格里大基地项目面向华电系统内外公开招聘106人笔试题库历年考点版附带答案详解
- 2025中建材(安徽)新材料基金管理有限公司招聘1人笔试题库历年考点版附带答案详解
- 2025中国联合网络通信有限公司贵州省分公司校园招聘(81个岗位)笔试题库历年考点版附带答案详解
- 2025年电子竞技行业电竞产业与全球赛事研究报告
- 2025年传媒行业传统媒体数字化转型探讨报告
- 2025年智能物流行业智能物流系统应用与全球物流发展研究报告
- 2025年肿瘤康复学化疗不良反应处理模拟测试答案及解析
- 2025年房地产行业政策调控与城市发展规划研究报告
- 2025年建筑行业建筑设计与城市规划研究报告
- 《高级会计学(第9版)》习题答案
- 快手磁力聚星星选达人认证考试试卷答案
- 一元二次方程-相似三角形-锐角三角函数复习
- 冰皮月饼的制作方法课件
- 在职党员到社区报到登记表“双报到”登记表
- 降低10炉脱硝系统液氨消耗量0
- 地下储藏室产权使用权转让协议
- 高考专题复习:小说专题训练人称的交替使用
- 大数据在高速公路收费稽查打逃方面的应用
- TY/T 3001-2006中国青少年儿童 手腕骨成熟度及评价方法
- GB/T 9722-2006化学试剂气相色谱法通则
评论
0/150
提交评论