程序员笔记
今天是第一天补课,终于有又机会可以听到林老师的课了,我觉得他比老潭说得还要好呢,虽然我没有听过老潭的课,不过我相信绝大部份在校的人学C语言都是用老潭的《C程序设计》吧。这本书的好处是有很多的,最主要的一点就是可以用生动的例子来说明一些概念,不过还是一点不好的地方,就是本书全都只是围绕着基础来说,没有一些可以让同学深入研究的课题。就我知道机械工业译的一本《C语言设计教程》,这本书有大量的实例练习,而且是围绕着生活的。学习和乐趣合在一齐,我在看这本书时都有好几个特别吸引我的兴趣课题呢!书就介绍到这里吧,还是说回今天补课的情况。
今天因为第一天吧,老师还不太清楚我们的底究竟到那里。是因为我们之前都是全自学的,所以现在要摸一摸底吧。一开始,他直接引入了上界程序员考试的下午的第一道题,是一道编程填空题。如下:
int strcmp( char *s, char *t)
{
while(*s && *t && _______)
{ s++;t++;
}
return ________;
}
这是模仿C语言字符函数库里的字符比较函数,当时我第一时间就想到了一种方法,第一空因为大家都没有问题吧,*s和*t这两个都保持为逻辑真就行,表明这个存储单元是用字符的,大家都知道C语言里没有字符串这种变量的,只有字符数组,`\0`这个符号就是用来表明这个字符数组到了结尾了,这里又有一个新的概念要说说的了,就是C语言逻辑里非零的都为真,那么`\0`这个符号就是为零。所以填这个空就应该没有什么太大的难度了,跟着就是还要有一个条件退出循环,因为是比较大小,只要保持一样都继续,所以条件也很显示的可以写出来*s == *t。至于第二题当时我的思维就销定在条件运算符里,因为返回的值是有三种可以性的,大于返回正数,等于就返回零,小于就返回负数。知道了这三种可能就可以用条件运算符填了,我当时的答案是这样的 *s == *t ? 0: *s>*t ? 1 : -1 ,这是不是很长呢,其实我的答案我也不知道是否对,但是真正的答案是 *s - * t .知道答案为什么是这样吗?当时我也一时给答案吓住啦,因为当时我真想到是用它们本身的比较就可以得出结果(运用ACSII码),*s - *t 如果s指针所指向的单元如果是大于当然就是正数啦,跟着其它的原理一样,这里不再详细说明。
除了引用这道答给我们说了很多的基础知识外,还更详细地给我们介绍了指针,唉!为什么老师说的总是这么的清晰明白,如果当初可以老师教的话就可以走少很多弯路了。算了,说这些话都是没有用的,只有现在能学好就行了。大家都指针的基础还有些吧,这里重要的提一提老师今天反复强调的一个概念,就是指针就是指向地址的一个变量。好了,今天就到这里吧。
因为前天老师摸到我们的底的关系,所以今天要补一补前面的基础部份。他先是列出一个数据类型的表,如下:
| 整型
| 字符型
| 基本类型< | 单精度型
| | 实型(浮点型)<
| | 枚举类型 | 双精度型
|
数据类型< | 数组类型
| 构造类型< 结构体类型 (结构)
| | 共用体类型 (联合)
| 指针类型
| 空类型
上面这个表,基本类型是我们平常用得最多的,包括整型、字符型、实型(浮点型),就从这里最常用的数据类型说起吧。
说起C语言的数据内容就要说说计算机里存放的数据是究竟怎么一回事,大家应该都知道计算机只可以处理二进制的数吧,因为是硬件的关系(二态器件),这些只能有两种表示的状态,所以运用到计算机里就显得特别有用了。从现在开始我们要知道计算机处理的所有数据都是二进制数,那么他究竟是怎么运算的呢?老师先给一些十进制数转换为二进制数的几道题我们做,这些小儿科当然是没问题啦,很简单的就做了出来。老师当然知道我们是会做的了,但是其实是想我们在做这些题目的时候找出更简单的转换方法。例:
10111012 =(93)10 很简单的就可以计算出来了,我的方法就是传统的计算方法。它们都有自己的位权,第一位就是20,第二位是21,跟着的都如些类推,将有1的地方乘上该位的数跟着相加起来就等于93了。这里说说其实二进制的次方特别好算,就像我们的内存一样阶梯上去的,1-2-4-8-16-32-64-128-256-512-1024……你知道这规律吗,如果知道是不是计算起来特别别好办呢!
不过老师在这里提出了一个更好的方法,起码比一个一个加上去也快多了。就是将那个要转换的数变为全都是1111111,你知道这个数是多少吗?其实就是有一技巧在里面,把它看成10000000 减 1吧!那么是不是很快就知道10000000是多少呢,没错就是128嘛,再减1就是127了,在些基础上试着将原来的那个二进制数位为零的那两个数求出来,第一个零在第二位,所以是2,第二个零在第六位,所以是32,将其加起来被127减去就可以得出93了,是不是很简单方便呢(学到东西快交学费啊,哈哈~!)。你知道计算机里二进制有什么几种运算吗?我在这里告诉你,其实就只有这么的一种,就是加法运算(你不要告诉我你连二进制的加法也不会运算,其实就是蓬二制一嘛)。为什么这样说呢?其实二进制也有减法运算和乘除,但是计算机里有一种叫补码的方法,可以将减法运算变为加法运算,至于怎么实现教师也没有再深入讲下去了(在些补充,乘法也是利用移位来实现转为加法的)。
现在转入到C语言的整型数据里,C语言的整型数据是2字节的,就是16位,最多可以存储65536,他的范围是 -32768 到 32767 。C语言里分有符号类型和无符号类型,如果是没有符号的整数类型的范围就是0 到 65535 了。关于字符型数据,如果严格来说C语言里根本没有字符这种类型,因为他所存储的是它的ASCII码。直接可以用来和其它的数据类型运算,比如:
main()
{
char s=`A`;
int i=2;
s=s+i;
printf("%d",s); /*这里可以直接输出其ASCII码*/
printf("%c",s); /*这里的结果因为上面的语句改变了字符s的字符,输出的是`C`*/
}
那么更不要说字符串了,所以字符串在C语言里也只是用数组来表示,和其它的高级语言不同,有其的字符串类型,而且还是字符和字符串结合在同一种类型里。现在该说一下实型数据了,字型类型通常用在有小数位的一些数据。就像这题一样:
S=1/1+1/3-1/5+1/7……1/2n-1
这个程序是我写的:
main()
{
int n,i,s;
int r=1;
printf("please input: ");
scanf("%d",&n);
for(i=1;i<=n;i++)
{
s=s+r/(2*i-1);
r=-1*r;
}
printf("%d",s);
}
这个是考试里的:
void fun(float *sn, int n)
{
float s=0.0,w,f=-1.0;
int i=0;
for(i=0;i<n;i++)
{
f=___*f; /*这里填 -1 */
w=f / (2*i+1);
s+=w;
}
____=s; /*这里填 *sn */
}
考试里的两个空我都做对了,可是自己写的那个程序就有大问题了,就是答案用了整型数据,从答可知答案应该是小数啊,真的一时的糊涂就可以至命啊!我们几乎所有人都是错了这点,当然也要另类的错法,就是用回来以前QB的一些运算符,^ 这个是QB里的次方运算符,这可真的闹出笑话了。之后是要我们编一个主函数来调用这个函数。
我是我做的
main()
{
float s;
int n;
printf("please input:");
scanf("%d",&n);
fun(&s,n);
printf("%f",s);
}
这是这么几条简单的语句,不过就难道了几乎所有人(除了我)。你知道他们的答案吗?让我给大家展示出来吧
main()
{
float *ss;
int n;
printf("Please input:");
scanf("%d",&n);
fun(*ss,n); /*这里出了问题*/
printf("%f",*ss);
}
他可以还没有了解到C语言里的函数参数的问题吧,既没有定义一个可以存放结果的变量,参数方面也用错了,如果真的要用指针也得要指针指向一个存储单元才行吧。而且还不是传了地址,他而是试图传一个指针指向单元进函数里,这是绝对错误的。因为该调用的函数是地址,好了,下面给他的程序更正一下。如下:
main()
{
float *ss,s; /*这里多定义一个单精度的变量*/
int n;
ss=&s;
printf("Please input:");
scanf("%d",&n);
fun(*ss,n); /*这里出了问题*
printf("%f",*ss);
}
好了,就这样这条程序就完全正确了,不过要是为了节省空间就用我写的那条吧,因为不要多开消一个指针变量。今天写得特别的长,也特别的嗅,望大家见谅了。
很快的就到了第三天了,接下来的学习任务应该越来越重了。至于今天讲了些什么,现在想起来也觉得没有什么似的,可能因为我之前已经把这今天所讲的内容搞懂搞透的原因吧。不过也得把今天的写下来,也没有什么特别原因的,想有个回忆吧。
今天所讲的都是围绕着数组,我们在C语言里定义数组和其它高级语言定义的不同,这里示出C语言和其它语言的。
C语言 Foxbase
int a[10][10]; dim a(10,10)
是不是符号也不同了,我们以前用惯的都是小括号,但是现在突然来的是中括号真的是有些不习惯呢。但是谁叫我们是学C语言呢,不习惯都要得习惯了。还记得以前定数组根本就是不用理会它的地址,只知道用就行了,就算用错了也会编译出错。可是C语言可不是呢,一但你定义了一个数组之后,你就得好好的管住它,因为数组出了边界是绝对不会通知你的。数组的定义和调用方法也是很多,真是灵活多变,这里不再重复书上里的东西了。现在就定义一个数组来看看:
int a[10];
如这个表所示,数组定义之后有相对地址,而且数组名a就是存放这些地址的首地址。现在我们定义多一个整型指针变量 int *p; ,让他指向数组a,p=a; 我们试着让指针运算递增一个p++; 我们看到的结果是p指向了新的地址2003,原来的地址是2001,为什么递增一个就移向了2003呢?不是2002才是正确的吗?其实这里就说明了我们定义指针变量为什么要整型呢,是因为所有的指针运算也是看自己本身是什么类型的指针作出什么的运算,就是现在是整型类型,整型数据存储是需要2字节的,所以针指运算也是按这个方式来到进行,结果很显然就是往下移2了。其实这里说这么多,老潭那本书里基本上都有详细说明介绍,所以我一开始说只要自己有看过书的,应该也很容易明白了(反而上面可能给我说模糊了)。
好了,接下来我们做一些题目吧,这是今天老师给我们出的题,其实也是2001年程序员下午考试里出现过的题目。所以请大家自己也动手做做,多思考,看看谁的方法比较好。 在n行n列矩阵中,每行都有最大数,本程序求这几个最大数中的最小一个。
#include <stdio.h>
#define N 100
int a[N][N];
void main()
{
int row,col,max,min,n;
/*输入合法的n和n*n个整数的代码, 注,这里略了一部份到后面练习自己做回*/
for(row=0;row<n;row++)
{
for(max=a[row][0],col=1;col<n;col++)
if ( ) max=a[row][col];
if ( ) min=max;
else if ( ) min=max;
}
printf("最大数中的最小数为:%d\n",min);
}
这题可真有些难度,它的难就难在第二个空那里,相信第一个空绝大部分都会做,可是第二个空呢,真的下不笔了。当时看程序的最后继续两个空后面的语句为什么一样的呢,可真的没有想通,只是要死钻牛解尖,老是想着一定是用数组的,第一个循环里是行,跟着就是列了。可是还是想不到答案,因为我的思路已经大错特错了。最后老师还是说出答案,也说这题真的是比较难。第二空其实是填row= =0,为什么这样填呢,是因为这个矩阵里一开始要有一个BASE数做底,所以row= =0只出现一次,很自然的就成了第一个比较的基数,跟着这个if语句里的就是比较这几个最大数中的最小一个数了,第二个空填了出来当然答案也就随之可以出来了max<min。看来我现在功力去考中程还是白费心机吧,因为这只是第一大题啊,有很多难的题都在后几题。那么既然现在知道自己的弱点就应该去好好克服改正它,好了,这只是第一道练习题,跟着下面还有将略了的那部份编出来。
我所写的如下,因为考虑到整数类型界限的问题,我所编的所着重这里。
printf("please input n:");
scanf("%d",&n);
for(row=0;row<n;row++)
for(col=0;col<n;col++)
{
do
{
printf("please a[%d][%d]",row,col);
scanf("%d",&a[row][col]);
}while(a[row][col]<-32767 && a[row][col]>32767 );
}
接下的是第二题了,题目如下:
求n*n的对角线和
这题因为全由自己写,所以各种写法都有。在下面先写我的最基础简单的方法吧。
#include <stdio.h>
#define n 5
main()
{
int a[n][n];
int row, col;
int sum=0;
/* 输入略 */
for(row=col=0;row<n;row++,col++)
sum+=a[row][col];
for(row=0,col=n-1;row<n;row++,col--)
sum+=a[row][col];
if ( n%2 !=0)
sum-= a[n/2][n/2];
printf("%d",max);
}
这是最基本的方法了,两个循环跟着判断是否偶数来减去中间重复出现的一个数,这样就求得结果了
下面我写一个我同学编的还比较简单,而且方法独到的(反正所有人都没有想过这种方法,除了他)。这里主要写一写他的方法。
int sum=0,j;
for ( j=0; j<n; j++ )
sum+=a[j][j]+a[j][n-1-j];
if ( n%2 !=0 )
sum-=a[n/2][n/2];
够简单吧,一次循环就可以了,他的思路是这样的,比方有一个如下的矩阵