本站首页    管理页面    写新日志    退出


«July 2025»
12345
6789101112
13141516171819
20212223242526
2728293031


公告
 注意这一点:其实我不是ppmm

我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:^_^
日志总数:93
评论数量:316
留言数量:8
访问次数:607117
建立时间:2005年5月12日




[电脑知识]一份c学习笔记
软件技术,  电脑与网络

vain 发表于 2005/5/17 20:51:41

发信人: lingjie (owl), 信区: C_Prog标  题: 一份c学习笔记发信站: 两全其美 BBS (Thu Oct 28 20:43:41 2004), 本站(lqqm.net) 此文为在下今年暑假所写,很多观点尚不成熟,供各位参考 ~~~·~~~~~~~~~~~~~~~~~~~~~~~~~~~ 写在前面的话 作为计算机专业的学生来说,学习编程是一件不可避免的事情,然而,想成为真正的程序员(相信这是我们共同的理想),光凭我们在课堂里学到的那些东西是远远不够的,我们必须读大量的书,写大量的代码,经历一个艰难而快乐的过程之后,才能真正走入这个领域。因此利用好这个漫长的暑假,对于一个想学好编程的人来说是很有意义的。 为什么要学习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.h" int main() {      printf( "\0hello world " );      system("pause");      return 0; }  呵呵,什么也没有?是的。还记得字符串是怎么结束的吗?\0 "\0hello world "是个常量数组。但是字符串却是""。这章尽管非常简单,但是每个例子都经典之作,建议你每个都抄一遍,你能从代码中学到更多的东西。 第二章 走过类型和表达式的迷宫 这本书的第二章的内容是学习编程中最基础东西,任何一门语言都会告诉你他支持那些数据类型、那些运算、有那些特点、以及有那些不完善的东西。学习这些东西相对来说是单调了点,麻烦了点,但是只有通过了这座迷宫,你才能进入C这个神奇的世界。因此初学者的成功至少有一半来自"耐心"。呵呵,准备好了吗? 这章的内容还是非常简单的,但是作者的字里行间隐藏了很多重要的信息,不加注意就会从我们的眼皮低下溜了去,下面将一一列出以示强调。 变量和常量 很多人对于他们的区别很模糊,个人认为他们的主要区别在于是否分配内存空间,换句话说,就是是否存在左值。左值是什么?在第二章的从头到尾好像没找到这个名词,呵呵,你可以在附录中关于变量的条目中找到他,其实就是变量的地址,变量一旦被定义,左值就被确定了,一直到他的生存期结束。我们通常说的变量的值是指变量的右值。这才是我们能操作的对象。根据这个理论,那么就不难知道其实被const修饰的对象不是常量,他有左值,但是这里有个小麻烦,在本章的开头写明了被const修饰的是常量(第二章章第二段有个()说明),我查看了原版,并没有这个补充说明,看来应该是译者的理解,在《C专家编程》中的一个例子证明了我的想法是正确的,例子如下: #include "stdio.h" #include "stdlib.h" #define one 1 const 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.h" int main() {     int ix;     ix = strlen( "\0abc" );     printf( "this is %d\n", ix );     ix = strlen( "\007abc" );     printf( "this is %d\n", ix );     system( "pause" );         return 0; } 你会发现,两个差不多的字符串长度完全不一样,什么回事呢?第一个我们可以理解:\0是字符串结束符,因此其后的任何东西都不能算字符串的内容,因此长度为0。但是第二个呢?我们查了换码序列表就知道'\007'这个为一个字符,因此长度为4。这个时候问题来了,编译器为什么没把'\007'理解为'\0''0''7'呢?如果这样的话长度也将为0,我们又没人为的加分割符号,呵呵,显然这个和编译器的具体实现相关,凭我们现有知识无法弄明白这点,姑且留着,等待"悟"的一天吧,相信我,这绝对是一种享受。 关于++运算符 在很多教材上都有个看起来很经典的题目,其代码如下: #include "stdio.h" #include "stdlib.h" int main() {     int ix, iy;     iy = 1;     ix = ( iy++ ) + ( iy++ ) + ( iy++ );     printf( "this is %d\n", ix );     iy = 1;     ix = ( ++iy ) + ( iy++ ) + ( iy++ );     printf( "this is %d\n", ix );     iy = 1;     ix = ( ++iy ) + ( ++iy ) + ( iy++ ) ;     printf( "this is %d\n", ix );     iy = 1;     ix = ( ++iy ) + ( ++iy ) + ( ++iy ) ;     printf( "this is %d\n", ix );     system( "pause" );         return 0; } 呵呵,是不是很晕?这个本来无非为了说明先加后加的问题,这个地球人都知道,这里不加说明了,但是这样的程序本身就有很大的问题,编译器的运算并非一定是从左到右的(有些是按树的遍历来算的),因此你会发现不同的编译器结果会不一样,关于这个第二章的结尾有很完整的解释,我就不再多说了,总之,这个测试本身就违背了语言的特性。 第三章 当好机器的老板   无论什么时候我们都不该忘记我们是在学一门语言,而学语言的基本要求是:准确无误的用它来表示自己的意图,不仅要让机器读懂,也要让别人(只要他会c语言)读懂你的意思。记住,语言是用来交流的,不论是编程语言还是自然语言。现在让我们对这两个交流的对象分别作个分析,如何才能让他们明白你想干什么,打算怎么干。 对于机器来说,我们要做的相对要简单点,编程语言的语法比自然语言要简单的多了,一切都由顺序、选择、循环三种结构复合而成,初学者要做的只是走一个"抄写-改写-模仿-习惯"的过程而已。等这些语句成了你的习惯那就太好了,就像你说汉语的时候不会去考虑你用的是陈述句还是感叹句,呵呵,(这个让我想起了我糟糕的英语,汗~~~)。当然我们对机器要做的远远不止这些,让机器读懂这只是第一步而已,如何让机器按照我们的意思运行的更好、更快才是我们要追求的境界,当然,这个境界没有止境。得在经验中慢慢积累,下面只是提出几个个人的建议而已: 尽量使用局部变量 因为c语言有个特点,在同个域中的变量必须定义在所有处理语句之前(分程序 除外),这意味着在程序开始的时候就必须分配好所有的静态空间,而很多数据在程序中用很少,因此我们需要减少这些不必要的开销,灵活运用分程序可以将这些对象进一步局部化,比较下面两段代码:Code1:    #include "stdio.h" #include "stdlib.h" int 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.h" int 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语言建立的那天起对他的争论就没有停止过,它的向下穿越给我们带来了不少麻烦。以至于在《[ISBN]7-115-10627-4[/ISBN]》的第二章中把它说成是"多做之过",但是我们发现有时候它的功能还是不可代替的,比如判断一个数是否属于某个离散集合: #include "stdio.h" #include "stdlib.h" int 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 a[2][1],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; } 当然,习惯在IDE下编写代码的朋友们可以借助环境的自动缩进(在非windows环境下往往就没这么舒服,得自己手动缩进),让上面的东西稍微好一点: #include "stdio.h" #include "stdlib.h" int main() {    int a[2][1],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; } 但是,看上去还是有点乱,特别是表达式的写法,如果你是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 1 const 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 < SIZE_2; ++j )         printf( "%d", *( ( p + j ) + i * 2 ) ); } int main() {    int array[ SIZE_1 ][ SIZE_2 ];    fun2( fun1( &array[ 0 ][ 0 ] ) );    system( "pause" );    return 0; } 用SIZE_n来表示每一维的长度,使得我们不再解释循环中2、1从何而来。 注释 对于一些难以一下就理解的语句我们可以用注释辅助理解,但是个人认为注释不能写的太多,一方面你要信任你用户的能力,他们都是比笔者更好的程序员。我们只要在变量和函数的名字中稍加暗示,他们就可以理会你的意思,但对于一些自己看起来不太清楚的,估计在日后可能会引起麻烦的代码,必须加注释说明你的意思。 比如对于这句代码。我不敢肯定指针的遍历是否正确,则我必须注释说明 printf( "%d", *( ( p + j ) + i * 2 ) );//以一维指针遍历二维数组。 最后,我还是得说,这里写的东西只是笔者两年来的一些心得,不合适之处, 不要采纳,大家可以去读一本巨作《程序设计实践》。比我说这些要好上百倍。 第五章 程序设计思想初步 到现在为止,我们已经对语言的基本元素有了个比较完整的了解了,但是总是停留在表达式等细节方面,我们很难写出程序来,在今天任何一个程序都是个工程,如何组织我们已经掌握的这些基本元素,使得他们变成有一点功能的有机整体,这个就需要一个整体观念的设计思想,对于c来说第一步该是过程化程序设计思想,换而言之,就是函数的设计,在上篇文字中我们已经看到了,其核心问题是如何分解要解决的问题,写出各个有独立功能的函数,然后由进入接口函数(在控制台环境下,通常是main函数)组成完整的程序。但是光是这样,我们能解决的问题相当有限,因为在实际应用中,我们要处理的不是那么简单的内置类型(int,char等),而是比这些复杂的多的数据类型,因此第二步该是如何针对具体问题写出抽象模型,即ADT(抽象数据类型),进而实现基于对象的设计思想,而学习指针和结构就需要带着这样的思想去探索,下面将通过一个简单list(链表)的设计来简要的说明一下该如何建立一个完整的程序。 第一步,建立一个项目: 最好不要选择"控制台程序"模板,这样能使得你的设计思路清楚明白,记住你现在在学习,方便快速不是你该追求的东西。 第二步,确定要提供的接口 静下心来好好想一想。你的链表要提供那些接口、那些可以给用户修改的部分(如具体的数据类型),这些放在用户可见的list.h文件中。在本文中假设我们提供初始化、销毁、增加节点、删除节点、 插入节点、查找、和打印输出几项功能。那么在上面的工程里加入一个叫llst.h的文件,输入代码如下: #ifndef LIST_H #define LIST_H /*定义函数状态*/ #ifndef ERR #define ERR -1 #define OK 1; #endif   typedef int status; typedef void type;     /*用户可以根据具体需要更改此类型*/ typedef struct listitem {     type           date;      /*节点数据*/     struct listitem *next;    /*指向下个节点*/ } list_node;//链表节点 typedef struct {     struct listitem  *ptr;    /*链表头指针*/     int              size;    /*链表长度*/ } list;//链表 list* list_init ( void );     /*初始化*/ status list_destroy( list* ); /*销毁*/ status add_node( list*, const type );  /*加入一个节点*/ status delete_all( list* );//清空 status delete_node( list*, list_node* ); /*删除一个节点*/ status insert_node( list*, const type ); /*插入一个节点*/ list_node* find_node( const list*, const type ); /*查找*/ status list_print( const list* );    /*打印*/ #endif 第三步,实现接口函数 在工程中加入list.c文件。Include了上面刚刚建立的头文件,并实现每个极口,由于在通常情况下此文件并不是用户可见(这里把维护等问题除外),所以笔者没加什么注释。当然这个不是什么好习惯,这里过于简单,注释就显得有些多余。 首先是include需要的头文件: #include "stdio.h" #include "stdlib.h" /*严格来说上面该用尖括号,由于网页显示不得已为之*/ #include "list.h" 接下来是初始化和销毁的实现 list* list_init ( void ) {     list *p = ( list* )malloc( sizeof( list ) );     if( p == 0 )        return 0;     p->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.h" list* list_init ( void ) {     list *p = ( list* )malloc( sizeof( list ) );     if( p == 0 )        return 0;     p->ptr = 0;     p->size = 0;     return p; } status list_destroy( list *pev ) {     if( pev == 0 )        return ERR;     delete_all( pev );     free( pev );     return OK; } status add_node( list *p, const type date ) {     list_node *pev =        ( list_node* )malloc( sizeof( list_node ) );     if( pev == 0 )        return ERR;     pev->date = date;     pev->next = p->ptr;     p->ptr = pev;     p->size++;     return OK; } status delete_node( list *p, list_node *pev ) {     list_node *temp = pev;     if( pev == 0 )        return ERR;     pev = temp->next;     free( temp );     p->size--;     return OK; } status delete_all( list *pev ) {     int ix;     if( pev == 0 )        return ERR;     if( pev->size = 0 )        return ERR;     for( ix = 0; ix < pev->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; } list_node* find_node( const list *pev , const type date ) {     int ix;     list_node *p = pev->ptr;     for( ix = 0; ix < pev->size; ++ix )        if( p->date == date )            return p;        else            p = p->next;     return 0; } status list_print( const list *pev ) {     int ix;     list_node *p = pev->ptr;     if( pev == 0 )        return ERR;     if( pev->size == 0 )        return OK;     for( ix = 0; ix < pev->size; ++ix )     {        printf( "%d\t", p->date );        p = p->next;     }     printf( "\n" );     return OK; } 第四步 连接各个接口 自己写个main函数,由于个人的调试方式不同,这里不给出代码。只要确保每个函数都能正常工作就行了。 好了,到现在为止我们把一个数据结构的实现走了一遍。当然,为了简单文字。笔者减少了很多list该有的功能。 第六章 尽量利用能利用的资源 在上篇文字中,我们设计了一个非常简单的list,在设计的过程运用了在本书第五、六章的知识,这些东西是c语言中最难的部分,学术方面的讨论随处可见,指针的用法和特性多得让人无法记住,个人认为最好的方法是多实践,在实践遇到的问题往往就是最常见的、最重要的知识点,至于那些特别的特性,等熟悉了那些常见的后也就不难理解他们了。 本书的第七。八两章所述的内容严格来说是不属于语言本身的东西,是的,我认为该这么说,这个关系到对"库"的理解,库是什么?是别人已经写好的东西(类型、函数、常量等等),我们的程序可以根据他们提供的接口调用就可,以节省我们开发的时间和精力,但是必须明白,不是没有库,我们就不能写东西了。第八章的内容就是告诉我们如何根据具体的os写出类似标准的io库,是的,库必须是系统相关的,当然你可以最大限度的保证他的可移植性(这正是标准库的成功之处)。 当然,对于大多数程序开发而言,库的运用可能是程序设计水平高低的最重要的指标之一,没有人会笨到放着的东西不用,而化费大量的时间去自己写一个(当然作为学习研究则正好相反)。有一次,一个网友跟我说标准c++和c怎么也干不了,当我提出反对意见时,此人气势汹汹的质问:"你不用WIN API写个窗体给我看看!?"。我无言了,因为要说的太多了, 比如:难道API是凭空出现的吗?难道我写不出API就可以说c++和c无此能力吗?我无此荣幸,就算有,也不是片刻就可以拿出来给他证明的。因此我选择沉默,人类的任何成功都是建立在前人的基础上的,这样才有我们引以为傲的效率。 回到正题,标准库非常庞大(别的也小不到那去),os的系统调用也很多,对于这些我的建议是:记住常用的,别的用到的时候查手册之类的东西即可,比如,以c标准库为例,在本书附录B中提到的大多都需要记住。至于别的,大家可以去看看《c语言参考手册》。 --※ 来源:.两全其美 BBS lqqm.net.[FROM: 220.184.85.60]


阅读全文(2945) | 回复(1) | 编辑 | 精华
 


回复:一份c学习笔记
软件技术,  电脑与网络

blue(游客)发表评论于2005/7/4 22:02:57

谢谢楼主


个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


» 1 »

发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.047 second(s), page refreshed 144771295 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号