第六天 今天的整个课程只有这么的一道题,但是学到的东西确很多。下面给出这条题目: 字符数字转为整数数值(字符可以任意:比如"342A")遇到其它否数字取前数。 我所写的程序如下,自认为写得不错: #define N 10; int catio(const char *str) /*const 的作用是常数,所以这里的地址不会返回到实参里*/ { int num[N]; int i=0;j=1,n=0; for(;*str++;i++) { if(*str<48 || *str>57) break; /*判断是否数字数值*/ num[i]=*str-48; } for(i-=1;i>=0;i--) { n+=num[i]*j; j*=10; } return n; } 你们说是不是比较简单呢?现在看不出等看完以下的另一个程序先断定吧。如下: long catio(char c[]); { int n,d; char *q,*p; long e=1,s=0; for(q=p=c,n=0;*p!='\0' && *p>='0' && *p<='9';p++,n++,e*=10); while(n>0) { d=*q++; switch(d) { case 48: d=0;break; /*太长了,略*/ : : case 57: d=9;break; } s+=d*(e/=10); n--; } return (s); } 现在比较来看看,不过虽然这条程序是比我那个复杂,但是也有他的思路和可取之处。像在那个for循环了,一条命令带过很方便也很简洁。其实我们可以继续改造这个程序,我们跟着老师的思路一步一步的把它进化,现在看看如下: long catio(char c[]); { int n,d; char *q,*p; long e=1,s=0; for(q=p=c,n=0;*p && *p>='0' && *p<='9';p++,n++,e*=10); while(n>0) { d=*q++-'0'; s+=d*(e/=10); n--; } return (s); 这样是不是更简化了,那么还可以再简化下去吗?前面的我们是可以做出来啊,当是老师说还可以更简单,我们都只好怀着期待的心情去听了。他一步一步的说出来,第一就是在s+d*(e/10)这里可以变为另一种形式,s=s*10+d,如果按照这样又可以去掉一个多余的变量了,变量e就没有了。接下来的更不可意议了,我不知道怎么说,看看程序先吧。 long catio(char *c); { long s=0; for(;*p && *c>='0' && *c<='9';s=s*10+*c++-'0'); return (s); } 大家看到了吗?原来这么长的程序可以一再简化到这个地步,这就是C语言的灵活了(我好像已经说了好几遍了,真的没有办法,不得不赞叹)。 今天就是这么一题,可真的有意外惊喜呢!
第七天 今天终于都讲到C语言比较后的范围了,"函数"说是C语言的一切真的没错(可能有吧,我不知道)。很多书上都说着函数是C语言根本,就是说函数是构成C语言的。看以下这个程序: main() { printf("Hello World"; } main()就是C语言里最特殊的一个函数,是构成整个程序的关键。在C编译器里首先就是要找出这个主函数才开始执行编译,好了,说了一些书上原来的东西。现在我们就来看看C语言里的函数究竟是怎么的,如果我们从基础的说起也没有什么意思。那么我们就从函数的另一个特点说起,"递归函数"相信很多人都知道这个吧,看过老潭的教程应该都知道他经典的第一个递归程序吧: int abc(int n) { int s; if(n >1) s= n*abc(n-1); else s=1; return (s); } 从这个源程序很容易就看出有一个同自己名字的函数在里面,所以以后我们看到一个函数里面调用自己就是递归函数了。而且我们看一个递归函数就主要就是看它是否一个返回的条件,就好像一条又黑又深的山洞,我们前去探险如果往到底就一定要回头,就算是更深的也要返回啊!所以我们判定一个递归函数是否成立也常常是看它的返回条件。至于上面的那个源程序我也不想多说了,应该大家也看得明白。 这里就看看另一个利用递归函数做的题目吧,就是诺汉塔(老潭的书上也是有的)。 #include <stdio.h> void move(char x,char y) { printf("%c-->%c\n",x,y); } void hanoi (int n,char one ,char two,char three) { if(n==1) move (one ,three); else { hanoi (n-1,one,three,two); move(one,three); hanoi(n-1,two,one,three); } } main() { int m; printf("input the number of diskes:"; scanf("%d",&m); printf("the step to moving %3d diskes:\n",m); hanoi(m,'A','B','C'); } /*运行情况如下: input the number of diskes:3 回车 the step to moving 3 diskes: A-->C A-->B C-->B A-->C B-->A B-->C A-->C 书上说hanoi(n-1,one,three,two);是把"one"上的n-1个往"two"上移,接着move(one,three);然后是hanoi(n-1,two,one,three)即把"two"上的n-1个往"three"上移; |h(2,1,3,2)|h(1,1,2,3)=>move(1,3) <-----1------ | | move(1,2) <-----2------ | |h(1,3,1,2)=>move(3,2) <-----3------ |move(1,3) <-----4------ | h(3,1,2,3) | |h(1,2,3,1)=>move(2,1) <-----5------ | h(2,2,1,3)|move(2,3) <-----6------- | |h(1,1,2,3)=>move(1,3) <-----7------ | */ 注意以上是网上一个网友写的,并不是我写的。诺汉塔最不同的就是它多次调用自己,所以看起来也比较复杂一点。第一次我在看这条程序的时候也是看了老半天也看不懂,在网上看到一位朋友说他自己是真的拿了一些碟子自己试着移来看看,我也自己试着看,效果真的挺好(我当然没有笨那这么大的碟子啦,用我的光盘写上大、中、小),大家不访也试试看。这个程序一定要自己慢慢去理解它,祝大家早日理解它吧。递归函数部份我因为不太懂也不能说些什么了,现在来看看函数的另一个内容吧,就是函数的参数调用。我这里先给出一个程序先吧: int abc(int a,int b) { a=a=b; return( a+b ); } main() { int xy[]={3,5}; int s; s=abc(xy[0],xy[1]); } 这里的将xy[0]和xy[1]分别传入形参里,这里要说的一点就是和其它高级语言不同的,C语言的函数调用都是单向传递的。限实参传了给形参之后并不会返回到传进来的实参的,所以我们务必记住这点。又如下面一题: int abc(int a[]) { int i,j; /* 排序 */ } main() { int x[5]={3,5,1,2,4} abc(x); /* 输出 */ }
----------------解决方案--------------------------------------------------------
这条源程序可为什么会改变实参的数值呢?老潭的书里说的很明白,说是因为这是地址传递,但是我们老师不太认同这点,他说这个也应该是值传递,只不过这个值是比较特殊的一个值,是地址,所以传到形参时可以通过调用这个地址指向的元素而已。如果按老潭的这样说以下这条程序都叫地址传递啦? int abc(int *p) { *p=10; } main() { int a=20,*w; w=&a; abc(w); /*abc(&a)*/ printf("%d",a); } 指针P也只是一个值而已,这个值是地址。如果说这是地址传递,那不是应该w的地址传给形参吗?剩下来的大家自己想想吧。(这里也不能够说谁对谁错) 接下来说说变量的存储类别,其实这个知识点也挺容易理解的,不过可能给C语言太多的这样的关系弄的糊涂了。C语言里有变量的类型,变量的存储类别,变量的全局性还是局部性,是静态的还是动态的呢。一切都是C语言变量的东西,我们这回也该好好的结束了它。(我们大家一齐看书吧)这里有一条源程序大家看看吧,我不行了,我要好好休息一下才行了。 int abc(int a) { int i,j; scanf("%d%d",&i,&j); if(i>j) { int k=1,i=2,j=3; pirntf("%d\n",i*3); printf("%d\n",j*10); } printf("%d",k); }
第八天 200X年X月X日 今天回到学佼也没有讲课,因为老师忙着一些其它事,听说好像是多媒体比赛的吧,要今天上交了。那我们只好回到课室里自己看书了,不过在这段时间里我们都没有看什么书,只是大家聊了起来。我也插了嘴吹了几句,可是很快就没有心情了,唉!只好睡一睡吧。当我休息了一会发现老师都已经回来了,而且说让我们今天上机房。今天是第一次上机房,不过如果不是什么事我也不愿上机房,因为我觉得听老师讲课还好。我们上到机房,老师给了一条程序我们,喔!这不是前两天说要搞的那个诺汉塔吗!而且是结合了图形表示的。我们都兴奋起来了,开始研究着这条程序。我开始执行这个诺塔了,他给的参数不是很多,只是十个盘子而已,你知道我按了多长时间吗?我一直按着来看也看了快一个5分钟才看完啊,这个问题果然是复杂。看着这些图画演示让我更加清晰的明白了诺汉塔的原理,这里我不敢自私,我把源程序也COPY回家了,以下就是了: #include <conio.h> #include <string.h> char dd[10][20],space[20]; int a[11],b[11],c[11]; init() { int i,j; for(i=0;i<20-1;i++) space[i]=' '; space[i]='\0'; for(i=0;i<10;i++) { for(j=0;j<20-1;j++)dd[i][j]=' '; dd[i][j]='\0'; for(j=9-i;j<=9+i;j++)dd[i][j]='a'+i; } for(i=0;i<10;i++) a[i]=i,b[i]=-1,c[i]=-1; a[10]=2,b[10]=25,c[10]=50; for(i=0;i<10;i++) { gotoxy(a[10],10+i); cprintf("%s",dd[i]); } } move(int *s,int *d) { int i,j; for(i=0;s[i]==-1&&i<10;i++); gotoxy(s[10],10+i); cprintf("%s",space); for(j=0;d[j]==-1&&j<10;j++); j--; gotoxy(d[10],10+j); cprintf("%s",dd[s[i]]); d[j]=s[i];s[i]=-1; getche(); } void hanoi(int n,int *s,int *w,int *d) { int i; if(n==1)move(s,d); else { hanoi(n-1,s,d,w); move(s,d); hanoi(n-1,w,s,d); } } main() { clrscr(); init(); getche(); hanoi(10,a,b,c); getche(); } 最后除了看了这条程序,老师还给我们试着用TC调试程序了。你知道我以前调试程序是怎样的吗?我就是直接ALT+F9看看有没有出错,如果有就修改,没有则成功了。可是真天我真正认识到TC里调试程序的真正方法,其实TC里有一大推的调试工具,这是我以前一直没有用过的,只知道有一个就是一步一步执行,跟着其它就一无所知了。其实TC里可以把一些变量的值跟踪显示出来,这是调试程序的重要手段,以前不知道这个功能都是用笔写在纸上的,现在可以很方便准确的看出来了。
今天终于到了C语言的核心部份了,指针一直都是被学习C语言公认为最难的一个大重点了,如果假如我们不学C语言的指针的话,那我们可以说根本没有学过C语言了。不过话说回来,在我刚开始接触C的时候前面的基本语法倒是很快的过了,可是学到指针结合到数组里就傻了眼,因为我根本看不明为么可以有这么多的数组调用方式(结合指针)。其实我下面的三言两语也很难说的明白指针这家伙的,请大家在上机里多多调试看看,待增加了经验后再回头看看指针这章,相信也能全摸透了。因为我也是这样过来的,我还特别看了很多运用指针方面的源程序。 现在我们就从相对于二维数组来说比较简单的一维数组开始吧,先看看如何定义一个指向一维数组的指针吧。 int a[5]={1,2,3,4,5}; int *p; p=a; /*这里a因为是数组的变量名,它的值是这个数组的首地址*/ 跟着我们可以通过指针来改变数组的值 p++; *p=6; /*那么数组的第二个元素就等于6了*/ 这里的意思就是让指针向下移一个,这样一来指向了数组的第二个元素。我们再细一点看看它的地址,通过这个指针,即当前指向的元素的地址。那么地址又是怎么运行的呢?p++这个命令就是让地址往下移的了,如果按照数组a 的类型来看,数组a是一个整数的类型,占的空间是两字节,而p++就只加1,顶多都是到第一个元素的后一半里,哪里可以指到第二个元素呢?其实这里就关系到定义指针时的类型,我们这里定义的也是整型类型,"对啊,这里定义整型是对的啊,因为它要指向整形数据嘛,那么当然就是一定要定义这种类型啦",其实这并不是真正的答案,而且也不必一定要定义为跟指向的数据一样类型,我们完全可以定义指针的类型为其它的。就比如定义为float吧,不过这里执行p++就直接跳过了一个数组元素,那么现在我们来看看究竟是怎么一回事。其实我们定义的指针类型就是用来结合指针,进行一定规则的运算方式。这里很明显可以看出如果是定义int 类型的就可以到第二个元素,说明了p++不是简单的地址加一,而是先结合这个是什么类型才进行运算的,加一次就等于地址移了2位了。float道理一样移4位,所以得到的结合是移到第三个元素。再往下看看: a=a+1; 这里我们进行地址移位赋值,不过这条命令是错误的,C语言里数组名是一个地址常量,所以不以试图改变它的值。 接下来简单地说一说二维数组,因为我们今天的任务就是首先搞清一维数组先。现在我们先来定义一个二维数组 int a[2][4]; 这里我不再重复书里讲的东西,我讲一下老师给我们的那种思想。我们这样来看一个二维数组,就是一维数组的元素又为是一维数组,这样嵌套。当然其它的多维数维都是这样一直嵌套下去的了。我们先看看这个图如图第九天图一这样就很容易说明了为什么a[0] 和 &a[0]为什么是一样都是代表着地址,其实都只是首地址,这里从文字很难可以说通,但是从意义上就可以理解。我们把二维数组的整列都充当为一个一维数组,不把它看作二维,这样得出如下: a[1][1]; 充当一维 M为名 M[1]; /*调用第二个元素*/ 我们试着把所有都这样看作,定义这样的一个一维数组 int a0[4],a1[4],a2[4]; 这样一来,我们就知道a0、a1、a2都是首地址了。 好了,可能也越说越模糊了,如果看不明白还是按照自己原来的思想去考虑数组吧,这是因为每个人都有自己的的想法和理解。
----------------解决方案--------------------------------------------------------