第15章 输入语句
scanf的使用
(04分07秒)程序1
scanf的细节
(13分19秒)注意:
※有些同学在书写时,经常将scanf函数书写成:
scanf(“%d%d\n”,&a,&b);
后面多加了个\n,电脑会多要求用户输入一个回车,否则程序不会往下执行。
(15分20秒)※可以指定输入数据所占列数,系统自动按要求截取所需数据。如:
scanf(“%3d%3d”,&a,&b);
若输入:
5201314(回车)
系统自动将520赋给a,131赋给b。
(13分52秒)※与printf不同的是,输入数据时不能规定精度,如:
scanf(“%7.2f”,&n); /n为浮点型/
若输入:
123456(回车)
n的值并不是1234.56,而是乱码。
(18分40秒)※如果在“格式控制”字符串中除了格式说明以外还有其他字符,则在输入数据时在对应位置应输入与这些字符相同的字符。如:
scanf(“%d,%d”,&a,&b);
输入时应用如下形式:
3,4
如果用:
3(回车)4
变量b将得不到数据。
如果是:
scanf(“%d□□□%d”,&a,&b);
输入时应用如下形式:
3□□□4
(20分54秒)程序2
scanf格式输入
(25分45秒)※在输入数据时,遇以下情况时该数据认为结束:
(1)遇空格,或按“回车”或“跳格”(TAB)键
(2)按指定的宽度结束,如%3d,只取3列。
(3)遇非法输入。
(4)%c只取1列。
如:
scanf(“%2d%d%f”,&a,&b,&c);
若输入
12345.26 78
3个变量的结果:a为12,b为345,c为0.26
(31分40秒)※在输入long型数据时,格式字符必须是%ld;输入double时,格式字符必须是%lf。否则得不到正确的数据
(32分12秒)题目讲解:
(1)现有格式化输入语句:scanf(“x=%d□,sum□y=%d,line□z=%d”,&x,&y,&z);已知在输入数据后,x,y,z的值分别是12,34,45,则下列选项中是正确的输入格式的是:
A、12,34,45
B、x=12,y=34,z=45
C、x=12C,sumy=34,z=45
D、x=12□,sum□y=34,line□z=45
参考答案:D
(38分07秒)附加程序:scanf的用户哲学
作业
若变量已正确定义为int类型,要给a、b、c输入数据,以下正确的输入语句是:
A、read(a,b,c);
B、scanf(“%d%d%d”,a,b,c);
C、scanf(“%D%D%D”,&a,&b,&c);
D、scanf(“%d%d%d”,&a,&b,&c);
第16章 数据类型与表达式
无符号整数的范围
(00分00秒)回到《数据类型》中“整数类型的取值范围”,可以得出unsigned short的取值范围为0~65535。
(02分29秒)原因是:unsigned short占有2个字节,也就是16位,在计算机内部用(0000 0000 0000 0000)2表示0,用(1111 1111 1111 1111)2表示最大数。
(1)2表示1,(11)2表示3,(111)2表示7……(1111 1111 1111 1111)2表示65535,刚好是21-1、22-1、23-1……216-1,得出无符号整型的计算公式为0~2n-1(n为整型位数)。
可以用此方法推算出unsigned int(long)类型的取值范围。
补码
(05分22秒)补码中第一位表示正负数:0表示正数,1表示负数。
(08分14秒)补码原理(见PPT演示)
(14分26秒)要求一个负数的补码,将它的绝对值二进制按位取反再加1,正数的补码是自己。
(15分18秒)如求4位二进制数-3的补码:把(3)10=(0011)2按位取反得(1100)2,再加1得(1101)2,short型只是把4位扩展为16位(0000 0000 0000 0011)2,按位取反得(1111 1111 1111 1100)2,加1得(1111 1111 1111 1101)2。
(19分01秒)16位二进制数-32768的补码:将32768转成二进制得(1000 0000 0000 0000)2,取反得(0111 1111 1111 1111)2,加1得 (1000 0000 0000 0000)2
(21分06秒)补码的特点是可以把减法运算转换成加法,比如一个4位二进制减法(0011)2-(0001)2在CPU中会先取得(0001)2的补码(1111)2,将此变成加法运算 (0011)2+(1111)2得到(10010)2,因为运算器只有4个位,前面的1舍去,得到正确结果(0010)2,这样CPU中就可以省去一个做减法运算的元件。
(27分38秒)补码在数学和计算机硬件的书籍中有更深入的讲解,对些有兴趣的的同学可以去网上搜寻相应的文章书籍进一步学习。
不同类型的混合运算
(28分46秒)※赋值语句中,等号两边类型不同时,以左边类型为准。
(30分42秒)程序示例
如:
int a=300;
char b;
b=a; /不可预期的错误,但系统不会报错/
(35分00秒)※除法运算时,2/3的结果为0,2.0/3或者2/3.0结果都为0.66666……。这说明如果除号两边都是整数,结果只能是整数,除号两边有任意一个浮点数,结果就是精确的浮点数。
混合运算时也遵循上述规律:
double d=1.0+2/3;
会先算2/3,这时答案已经是0,再加1.0,得到错误的结果1.0,改成
double d=1.0+2/3.0;
结果为1.666666,正确。
(36分32秒)※char参与任何运算都会转换成ASCII码
※%(取余)不能用在浮点数上,它不会自动将浮点数转换成整数。
强制转换
(37分18秒)格式:
(类型名)表达式
如:
double d=(double)2/3+1.0;
相当于double d=2.0/3+1.0;
与double d=2/3.0+1.0;运算结果相同
作业
写出下列8位二进制数的补码,并转换成十六进制。
(1) (221)10=( )2=( )16
(2) (-128)10=( )2=( )16
(3) (-32)10=( )2=( )16
第17章 更多的运算符
复合运算符
(03分04秒)算术运算符后面加=号,便成为复合运算符
如:
i+=5;
相当于i=i+5;
那么i-=a+b;等价于什么?
注意:i*=a+b;并不等价于i=ia+b,而是等价于i=i(a+b)
(06分23秒)±*/%5个算术运算符都可以和=号组合成复合运算符,除此之外还有位运算符也可与=号组合成复合运算符。
逗号运算符
(08分15秒)逗号运算符的运算顺序为从左到右,计算结束后取最右边的表达式作为运算结果。
如:
int a;
a=(3+5,4+6);
a的结果为10。
(11分00秒)在循环中经常见到:
for(i=0;i<=100;s+=i,i++);
用这种方式计算1加到100的和,循环体可以省掉。
(17分20秒)※逗号运算符的优先级是C语言中最低的,比=号还低。假设a=3,下面的逗号表达式:
b=(a=2+5,a2)
会先算a=2+5,再算72,b的最终值是14,而不是3*2得6。
(21分00秒)※逗号表达式出现在函数调用当中,很容易混淆,如:
func(rec1,rec2+rec3,(rec4,rec5));
该函数调用语句中实际上只有3个函数实参,(rec4,rec5)是个逗号表达式,取变量rec5的值作为实参,差别就在于这个括号。
(23分48秒)※逗号是表达式,分号表示一条语句结束。
赋值表达式
(25分39秒)int a=1,b=2,c,d;
if((c=a)<(d=b)){…}
(27分13秒)if语句中,c=a表达式得到的结果为1,d=b得到的结果为2,相当于比较1<2,条件成立,会执行花括号中的语句。
※表达式int a=b=c=1+2;的真实运算顺序如下:
int a=(b=(c=(1+2)));
(30分45秒)※赋值表达式也可出现在函数中,如:
printf(“%d\n”,(n=5));
运算符结合性
(31分29秒)单目运算符(++,–,!取反,&取址,*取值,正负号)、条件运算符(在后面讲解)和=号(包括复合运算)结合性都是从右向左,其它运算符都是从左向右结合,如:
a=-i++相当于a=-(i++);
(34分00秒)上节的int a=b=c=1+2+3;先算1+2是因为=号的优先级比+号低,先算后面的c=1+2是因为=号的运算符结合性是从右向左。
++和- -
(36分34秒)i++等价于i=i+1
i–等价于i=i-1
++i表示i先自加1,再参与运算
如:
int i=2;
printf(“%d\n”,++i);
输出3,i的值为3,这条printf语句可以分解为:
i=i+1;
printf(“%d\n”,i);
(40分26秒)※注意i++和i+1的区别。
i++表示i先参与运算,再自加1
printf(“%d\n”, i++);
输出2,但是i的值为3,它可以分解为:
printf(“%d\n”,i);
i=i+1;
(42分53秒)※++和–只能作用于变量,不能作用于常量或表达式,如3++或(a+b)–都视为非法操作。因为++和–都会产生新值,常量或表达式都无法存放这个新值。
(45分00秒)※如果遇见a+++b,编译器会尽可能地从左到右组合运算符,把它变成(a++)+b,这和运算符结合性没并有什么关系。如果你忘记了这条法则,可以自己使用括号控制运算顺序。
(46分23秒)题目讲解:
设变量已正确定义为整型,则表达式n=i=2,++i,i++的值为____
参考答案:3
(48分25秒)程序1
++运算符演示
作业
用C语言的方式写出一元二次方程的一个实根:
第18章 选择的嵌套
逻辑表达式
(01分40秒)※C语言中没有其它语言的“真”和“假”值,英文叫TRUE和FALSE值,也叫逻辑型变量。所有的表达式,只要非0(包括负数)即为真,0即为假。真和假就是我们平时所说的“条件成立”与“不成立”。
(04分08秒)※关系和逻辑运算符也可以和算术运算符一起参加混合运算,与算术运算不同的是,关系和逻辑运算的结果只有0和1。
假如int a=3,b=4,c=0;
a&&b的结果为1,b||c的结果为1,a&&c的结果为0。!a+c>b||a-b的结果呢?
(07分47秒)※ a && b && c中,如果a为假,就不必再判断b和c,对于(i=0) && (j=20)这样的式子,j=20这条赋值语句永远也不会执行。
(09分35秒)※同理,a || b || c,如果a为真,就不必再判断b和c,对于 (i=5) || (j=20)这样的式子,后面的j=20也不会执行。
条件运算符
(13分17秒)格式:
条件表达式1 ? 表达式2 : 表达式3
它相当于普通的if分支语句,条件表达式1成立(非0值),执行表达式2,不成立(0值),执行表达式3。如果将它作为表达式,将执行过的值作为整个表达式的最终值。
(15分14秒)条件表达式应用的一个典型例子就是取两数中的最大值:
int max,a=3,b=5;
max=a>b?a:b;
它相当于
if(a>b)max=a;
else max=b;
(17分39秒)题目讲解:
设int a=5,b=6,表达式(++a= =b- -)?++a:- -b的值是____
参考答案:7
嵌套的if
(26分25秒)定义:if中还有一层if,或者else中还有if
(26分32秒)程序1
帐号检查
(28分42秒)讲解流程图
(30分40秒)讲解程序
(38分54秒)附加程序:“配对”写程序技巧,以减少错误
(40分00秒)※嵌套的层数过多,if语句的配对容易出问题,这种问题主要出现在if和else数目不一样的情况下,如:
if (a<5)
if (b<5) printf (“b\n”);
else
if (c<5) printf (“c\n”);
else printf (“d\n”);
程序作者想通过格式的改变将第一个else和第一个if语句配对,实际上编译器有自己的配对方式:它会将else与离自己最近的if语句配对,上面的语句实际上被编译器配对成:
if (a<5)
if (b<5) printf (“b\n”);
else
if (c<5) printf (“c\n”);
else printf (“d\n”);
(43分00秒)如果想强制配对,请多使用花括号:
if (a<5)
{
if (b<5) printf (“b\n”);
}
else
{
if (c<5) printf (“c\n”);
else printf (“d\n”);
}
作业
1、有以下程序:
#include <stdio.h>
main()
{ int a=2,b= -1,c=2;
if (a<b)
if (b<0) c=0;
else c+=1;
printf (“%d\n”,c);
}
程序的输出结果是
A) 0 B) 1 C) 2 D) 3
2、编写程序:输入a、b、c三个数,打印出最大者。
第19章 多路分支
if多路分支
(00分42秒)格式:
if(表达式)
{
若干语句
…
}
else if(表达式)
{
若干语句
…
}
else if(表达式)
{
若干语句
…
}
else
{
若干语句
…
}
(02分12秒)程序1
学校将考试成绩分为3档,0-59分为不及格,60-89分为及格,90-100分为优秀。现在请编一程序,由老师输入一个学生成绩,计算机判断这个成绩在哪一档,并在屏幕上显示。
(03分02秒)讲解流程图
(05分28秒)讲解程序
switch语句和break语句
(14分58秒)格式:
switch (表达式)
{
case 常量表达式1:语句1
case 常量表达式2:语句2
……
case 常量表达式n:语句n
default:语句n+1;
}
(18分15秒)程序2
写一个程序供老师查询A、B、C三档分数各在什么范围
(19分02秒)讲解流程图
(20分59秒)讲解程序
注意:
(25分16秒)※switch语句出现嵌套时,break只会跳出本层switch
※default相当于if中的else
※break不对if语句起作用。
(26分52秒)※case如果后面没有break,程序会继续向下执行,而不会跳出switch,如:
int a=1;
switch(a)
{
case 1:printf(“1\n”);
case 2:printf(“2\n”);
}
这时1和2都会打印在屏幕上。
(28分18秒)※case后面只能是常量表达式,不能是变量,如:
case 1+2:
case ‘a’:
case 1<2:
都是合法的,而
switch(a)
{
case a:
case a<5:
}
因为a是变量,两条语句都不合法
(36分19秒)程序3
投票统计
(52分20秒)演示break不对if语句起作用
(54分36秒)题目讲解:
以下程序的运行结果是_____。
#include <stdio.h>
main()
{ int x=1,y=0,a=0,b=0;
switch(x)
{ case 1:
switch(y)
{ case 0:a++;break;
case 1:b++;break;
}
case 2:a++;b++;break;
case 3:a++;b++;
}
printf(“a=%d,b=%d\n”,a,b);
}
参考答案:a=2,b=1;
作业
若a值小于100,请将以下选择结构改写成由switch语句构成的选择结构。
if (a<30) m=1;
else if (a<40) m=2;
else if (a<50) m=3;
else if (a<60) m=4;
else m=5;
提示:既然a的值小于100,switch后一对括号内的表达式可写成a/10,这样可精简代码。
第20章 循环的嵌套
六种排错方法
(03分55秒)1、断点
2、单步执行:跟踪
(07分21秒)3、缩小循环变量
4、注释代码段排错:整片整片地“删除”代码,如果在注释掉某片代码,程序突然不出错了,说明问题出在那片区域
(09分42秒)5、printf
6、输出到文件:用printf输出的结果在一屏幕显示不下的时候,就将它输出到文件
(10分23秒)注意:调试时只能按F5,不能按Ctrl+F5
循环嵌套和break语句
(11分50秒)while、do while和for语句都可以形成嵌套,在这里我们着重分析for语句的嵌套。
(12分20秒)程序1
简单的for语句嵌套
(22分05秒)第二段小程序
(27分15秒)讲解流程图
(31分10秒)程序2
打印九九乘法表
尝试用F10跟踪此程序的执行流程
注意:break语句不是跳出if中的花括号,而是直接跳出本层循环
(47分04秒)程序3
小游戏:打飞碟
数学类问题解题技巧
(77分55秒)一、找到计数器(步长)的数学规律,确定步长
二、确定初值和终值
三、找出“总和”或“总积”的数学规律
(78分42秒)找规律:
(一)1加到100:1+2+3+4+…+99+100
(二)计算PI值:,直到某一项的绝对值小于10-6为止
(三)求某数列的前40个数:1,1,2,3,5,8,13,21…
(四),计算前50项
(五)求100~200间的全部素数
(84分36秒)题目讲解:
1、以下程序的输出结果是
#include <stdio.h>
main()
{ int a,i;a=0;
for(i=1;i<5;i++)
{ switch(i)
{ case 0:
case 3:a+=2;
case 1:
case 2:a+=3;
default:a+=5;}
}printf(“%d\n”,a);
}
参考答案:31
(88分08秒)2、以下程序的输出结果是:
A、12 B、15 C、20 D、25
int i,j,m=0;
for(i=1; i<=15; i=i+4)
for(j=3; j<=19; j=j+4)m=m+1;
printf(“%d\n”,m);
参考答案:C
注意:这里的循环省掉了大括号
作业
1、编程题:求e的值。
(1)用for循环,计算前50项
(2)用while循环,要求直至最后一项的值小于10-4
2、(单重循环思考题):给一个不多于5位的正整数,要求:(1)求出它是几位数;(2)分别打印出每一位数字;(3)按逆序打印出各位数字,例如原数为487,应输出784。
第21章 函数的嵌套
模块化与函数嵌套
(00分00秒)计算机的最终走向是模拟人工智能和社会,人类在完成复杂任务都采用分工合作的方式,在计算机内部也可以通过函数来划分各程序的功能来完成一个复杂任务。
main函数就相当于程序里的皇帝,必须要有,并且只有一个。它指挥所有的大臣(子函数)协调工作,大臣又可以调用更底层的子函数,相当于指挥小兵再进行更具体的工作,这就叫函数嵌套:
(04分14秒)程序1
函数的嵌套调用
(11分32秒)程序2
打印100-200间所有的素数(两种解法)
(23分56秒)方法2程序演示
再论数据传递
(30分20秒)int fun(int n)
{
int m;
m=n+2;
return m;
}
main()
{
int i=3,j;
j=fun(2)*fun(i)+6;
}
第一次调用:
定义:fun(int n); /形参/
调用:fun(2); /实参/
这个实参到形参的传递过程可以分解为
int n=2;
第二次调用
定义:fun(int n); /形参/
调用:fun(i); /实参/
这个实参到形参的传递过程可以分解为
int n=i;
返回值没有形参和实参的说法,返回细节也比参数传递复杂,在这里不作详解。如果在表达式中出现函数调用,直接用它的返回值代替函数即可,如:
j=fun(2)fun(i)+6;
两次返回值传递后得:
j=45+6;
(36分12秒)程序3
求3个数中的最大数
(45分25秒)如果实参与形参类型不匹配,会发生什么样的事情?
(46分42秒)按数据类型的精度来排序,顺序如下(不考虑unsigned):
char、short、int、long、float、double、long double,如果形式参数比实际参数精度高,系统会自动转换成正确的类型,比如整数5会被转换成浮点的5.0。反过来如果形式参数精度比实际参数低,系统也不会报错,但是所得到的数值无法预料。
比如:
void fun(int a){…}
下面的代码:
short s;
fun(s);
没有问题,但是:
double d;
fun(d);
编译可以通过,程序在执行却会有无法预料的后果。
(52分18秒)下半节课开始
(52分58秒)题目讲解:
填空题:
请补充函数fun,它的功能是:计算并输出n(包括n)以内所有能被3或7整除的自然数的倒数之和。
例如,在主函数中从键盘给n输入30后,输出为:s=1.226323。
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的横线上填入所编写的若干表达式或语句。
试题程序:
#include <stdio.h>
double fun(int n)
{
int I;
double sum=0.0;
for(I=1;【1】;I++)
if(I%3= =0【2】I%7= =0)
sum+=【3】/I;
return sum;
}
main()
{
int n;
double s;
printf(“\nInput n:”);
scanf(“%d”,&n);
s=fun(n);
printf(“\n\ns=%f\n”,s);
}
参考答案:
(1)I<=n
(2)||
(3)1.0
递归函数
(64分10秒)函数还可以自己调用自己,这样的函数称作“递归函数”。
(65分50秒)程序4
用递归函数求10!(见PPT演示)
(75分03秒)※注意:递归函数必须要设条件返回,否则它会无限递归下去,直至程序崩溃。
(75分30秒)题目讲解:
※以下程序的输出结果是 【1】 。(见PPT演示)
#include <stdio.h>
void fun(int x)
{ if(x/2>0) fun(x/2);
printf(“%d”,x);
}
main()
{ fun(3); printf(“\n”);}
参考答案:13
函数的设计原则
(86分10秒)函数在设计时应该遵循两种原则:一种是自顶向下,也就是在主函数中先设计功能,再设计子函数,如一个煮饭程序:
main()
{
洗米();
下锅();
打开电饭煲();
}
将中文翻译成英文后,再在main函数顶部分别再设计各子函数的细节:
void 洗米()
{
……
}
(87分44秒)另一种是由下而上:许多通用函数设计出来时根本就不知道哪个父函数谁会调用它,比如我们看到的sqrt、printf和各种库函数,程序员将它设计好后把函数的接口(声明部分)交给调用者,调用者不需要知道里面的程序细节,只需按照它的接口调用即可达到目的。
有些函数设计出来,有可能永远都不会被调用。这就好像if语句的分支一样,有些分支因为数据值没有满足条件,就永远都不会执行那个模块。这也是对程序设计资源的一种浪废。
目前无论是汽车、手机、电脑都是按照这个理念组装出来的。
作业
(1)上机填空题:请补充函数fun,该函数的功能是:计算并输出下列多项式的值。
例如,若主函数从键盘给n输入50后,则输出为S=1.960784。
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的横线上填若干表达式或语句。
试题程序:
#include <stdio.h>
【1】 fun(int n)
{
int i,j;
double sum=0.0,t;
for(i=1;i<=n;i++)
{
t=0.0;
for(j=1;j<=i;j++)
t+=【2】;
sum+=【3】;
}
return sum;
}
void main()
{
int n;
double s;
printf(“\nInput n: “);
scanf(”%d”,&n);
s=fun(n);
printf(“\n\ns=%f\n\n”,s);
}
(2)上机改错题:下列给定程序中,函数fun的功能是:根据整型形参m,计算如下公式的值。
例如,若m的值为5,则应输出:1.463611。
请改正程序中的错误,使它能得出正确的结果。
注意:不要改动main函数,不得增行或删行,也不得更改程序的结构。
试题程序:
#include <conio.h>
#include <stdio.h>
double fun (int m)
{ double y=1.0;
int i;
/found*/
for(i=2;i<m;i++)
/found*/
y+=1/(i*i);
return (y);
}
void main()
{ int n=5;
system(“CLS”);
printf(“\nThe result is %1f\n”,fun(n));
}
第22章 多维数组
二维数组
(00分00秒)定义:
类型名 数组名[常量表达式] [常量表达式];
如:
int a[3][3];
它定义了一个有9个元素的二维数组(3*3),两个维数的下标都是0-2
初始化:int a[3][3]={{0,1,2},{3,4,5},{6,7,8}};
或
int a[3][3]={{0,1},{5},{6,7,8}};
它相当于
int a[3][3]={{0,1,0},{5,0,0},{6,7,8}};
(03分46秒)提问:
int a[2][4]={{0,1},{0}};
数组a的下标各是从多少到多少?各数组元素的值是多少?
多维数组的规律
(05分20秒)※数组其实也是一种嵌套结构,二维数组也叫数组的数组,那么三维数组呢?n维呢?
※像Photoshop这样的平面图像处理软件中,经常用到二维数组,处理二维数组通常要用到双重循环。
※像3D Max这样的三维图像处理软件中,经常用到三维数组,处理三维数组通常会用到三重循环。
(07分42秒)※类似Windows资源管理器中的“树状菜单”,经常会用到多维数组。
(09分48秒)程序1
将一张矩阵笑脸上下翻转成哭脸
(16分42秒)讲解程序,前面先讲解题思路
a= 翻转成 b=
解题思路:
矩阵坐标为:
翻转规律为
放置第一排
b(3,0)=a(0,0)
b(3,1)=a(0,1)
b(3,2)=a(0,2)
b(3,3)=a(0,3)
放置第二排
b(2,0)=a(1,0)
b(2,1)=a(1,1)
b(2,2)=a(1,2)
b(2,3)=a(1,3)
放置第三排
b(1,0)=a(2,0)
b(1,1)=a(2,1)
b(1,2)=a(2,2)
b(1,3)=a(2,3)
放置第四排
b(0,0)=a(3,0)
b(0,1)=a(3,1)
b(0,2)=a(3,2)
b(0,3)=a(3,3)
也就是说把矩阵a中第0行的数据放到矩阵b中的第3行,第1行放到第2行,第2行放到第1行,第3行放到第0行。经过4次重置,任务完成。
下标在交换过程中有几个规律可寻:
第一个规律:在4次放置中,b的第一位下标刚好是递减的,a的第一位下标刚好是递增的。无论在哪一排,b的第一位下标数字刚好是3减去a的第一位下标数字。(请同学们思考这个3的意义是什么?)
第二个规律:两个矩阵的第二个下标数字相同。
排序
(30分42秒)程序2
将任意5个数按由大到小的顺序排序(见PS演示)
(37分12秒)讲解程序,前面先讲解题思路
(32分04秒)解题思路:
(1)将第1个数与后面所有数比较,如果有比它大的,就与它交换
(2)将第2个数与后面所有数比较,如果有比它大的,就与它交换
(3)以此类推,将第n(n=1~5)个数组元素与n+1到n+5个数比较,较大的数字会被选择到第n个元素中
(4)全部比较完成后,数组已经按从大到小的顺序排好,用循环输出数组
用5个数来描述以上过程:
1)57428:5和7比较得:
2)75428:7和42比,还是原来顺序,与8比较得:
3)85427:第一轮比较完毕,老大已经出现在第1位。第二轮开始:5跟42比较,还是原来顺序,与7比较得:
4)87425:第二轮比较完毕,老2已经出现在第2位。第三轮开始:4跟2比较,还是原来顺序,与5比较得:
5)87524:第三轮比较完毕,老3已经出现在第3位。第四轮开始:2跟4比较得:
6)87542:第四轮比较完毕,老4已出现在第4位。最后一位不用比,排序结果已经出来。
这就是世界著名的“选择排序法”
排序方法有多种,还有“冒泡排序法、快速排序法”,因为时间问题,在这里无法一一列举。
数组与函数
(50分34秒)数组元素可直接做函数实参传递,下面的代码是一维数组和二维数组元素传递给普通函数:
int a[10]={5,10},b[10][10]={{2,3},{5,6}};
int c;
c= max (a[1],b[0][0] );
有时候要将整个数组传进函数,这时函数的形式参数要改变。
(52分56秒)程序3
求5个学生的总分
(57分17秒)※函数的形参可以省略元素个数,如:
double sum(double array[])
也合法
(58分47秒)程序4
将两个矩阵相加
(66分21秒)※数组元素做函数参数时,如果子函数改变了数组元素的值,父函数里的数组元素不会变。但是用数组名做函数参数时,父函数里的的数组也会随之改变。
(67分45秒)※函数定义时,二维和多维数组的第一维元素可以省略。
作业
1、将程序1的矩阵左右翻转。
2、已有一个按从小到大排好序的数组,今输入一个数,要求按原来排序的规律将它插入数组中。比如数组a元素为2,5,7,12,20,0(多出个0是为了方便插入数据用),用户输入10后,数组变成了2,5,7,10,12,20。
3、熟悉程序2的排序程序,争取不看书能独立将其写出。(提醒:写如此复杂的程序加上打草稿时间,至少要1小时以上)
第23章 结构的嵌套
(00分00秒)技巧:要养成写注释的习惯
(03分45秒)技巧:Alt+F8自动排版
结构数组
(08分58秒)既然结构是一个自定义“类型”,那么我们可以像int那样,定义一个结构数组:
struct student ArrayStu[10];
上面的代码一次定义了10个学生。如果同时给这个数组的前2个元素初始化,就写为:
struct student ArrayStr[10]={{“张三”,”男”,18},{“李四”,”女”,22}};
(12分58秒)结构数组元素使用时和普通变量一样要加上下标:
ArrayStu[1].age=18;
它把李四的年龄改成了18岁
在实际应用中,初学者有时会将上面的代码误写为:
ArrayStu.age[1]=18;
下标写在后面,其实跟写在前面有完全不同的意义,后面会详细解释这种写在后面的意义。
结构嵌套
(14分43秒)struct Student /学生/
{
char name[10]; /姓名/
char sex[3]; /性别/
int age; /年龄/
};
struct Room /寝室/
{
int number; /号码/
struct Student stu; /学生/
};
main()
{
struct Room room;
strcpy(room.stu.name,”张三”);
/如何输出张三请同学们自己思考/
}
(20分08秒)※这里举的是结构嵌套结构变量的例子,结构也可以嵌套结构。考虑到这种方式较少使用,在这里不做详解。
结构成员为数组
(22分49秒)我们将学生结构定义修改一下:
struct student /学生/
{
char name[10]; /姓名/
float score[8]; /本学期8门课成绩/
float aver; /平均分/
};
(23分58秒)此结构表示每个学生将本学期8门课的考试成绩放在score数组中,最后通过程序算出平均分存进aver。(见Excel演示)
(28分47秒)程序1
计算学生平均分
(39分28秒)题外话:结构与函数的哲学
(41分55秒)题目讲解:
根据以下定义,能输出字母M的语句是
A、printf(“%c\n”,class[3].name);
B、printf(“%c\n”,class[3].name[1]);
C、printf(“%c\n”,class[2].name[1]);
D、printf(“%c\n”,class[2].name[0]);
struct person{char name[9];int age;};
struct person class[10]={“John”,17,
“Paul”,19,
“Mary”,18,
“Adam”,16,};
参考答案:D
作业
有5个学生,每个学生的数据包括学号、姓名、成绩,从键盘输入5个学生数据,要求将最高分学生的学号、姓名、成绩全部打印出来。
第24章 枚举
(00分00秒)解析浮点型数据
枚举的作用
(02分15秒)有一种变量类型,它的值只有固定的几种选择之一,比如性别(男、女)、硬币的两面(正、反)、星期(周一到周日)、居住省份(36个省市自治区名称)等等,这样的变量适合用enum枚举类型。
使用枚举
(03分43秒)说明:
※自定义的类型一般都放在main函数前面,枚举也遵循这个规律:
enum sex{boy,girl};
main(){…}
(05分47秒)※枚举类型在定义时,系统会自动用整数为枚举的可用值从0开始用整数进行编号,如上面的boy的值为0,girl为1,也可以用printf(“%d”,gril)这样的语句输出。
枚举值也可以叫做“符号常量”,有时候用英文单词组成的符号常量来代替简单的数字,会使程序的可读性大大提高。比如我们看到boy第一反应这就是男孩,而你看到数字0,并不知道这表示什么意思。
(09分00秒)可以通过赋值来改变枚举常量的值:
enum color{red=4,green=1,blue,black};
这条语句中从blue开始会自动往后递增,blue的值为2,black为3。
(09分50秒)※不能给枚举常量赋值,比如boy=2,这是错误的写法。
※枚举常量相当于一个关键字,程序中不得出现与枚举常量相同的标识符。
※枚举支持所有的逻辑运算,也可以递增递减,比如上面的枚举类型color值为green,color++就得到blue,color+=2就得到black。有了这个规律,我们就用循环来处理枚举变量就非常方便。
(13分28秒)程序1
箱子里有3种不同颜色(红绿蓝)的乒乓球若干个,请任意摸3个,如果摸到2个相同颜色的为二等奖,奖金一千元,摸到3个相同颜色的为一等奖,奖金一万元。
(27分53秒)※仔细观察程序1会发现,枚举常量不支持直接的输入输出,因此,枚举变量经常会跟swith联用而达到直观的文字输出效果。
作业
我国规定周六和周日双休日,请输入一个星期几,由程序判断并输出这一天是否是休息日。
第25章 指针基础
指针的作用
(00分46秒)※处理大量数据:编写大型软件时,函数与函数之间经常会传送大批量的数据。比如一个班有50个学生,有一个子函数的作用就是将这50个学生的成绩进行排序,再返回给父函数。如果用之前学过的“参数复制”方式,我们就要定义一个有50个参数的子函数,并且子函数只能有一个返回值,不能实现这个功能。
这时候父函数可以将第一个同学的座位号(指针)传给子函数,一个参数就能搞定。
(02分58秒)※改变父函数中变量的值。
※高手用来提高程序运行速度。
内存和地址
(04分16秒)※许多馆子店在管理房间时,会给每个房间编号,同时会给房间取个名字,比如“水云阁”,“碧玉居”等等。那么名字就是我们平时所说的“变量名”,编号就是我们所说的“指针”,俗称“内存地址”。
(05分50秒)※指针也是变量,它也要占用内存,它不存储“整数”,“实数”这种普通数据,它存的是其它变量的“地址”,占用4字节内存。
指针的定义和使用
(07分37秒)普通变量定义时,前面加个*就是指针的定义。一般来说,我们都会在指针类型的变量名前面加p,表示pointer(指针)的意思。
指针也是变量,它也要占用内存,只是它不是存“整数”,“实数”,它存的是其它同种类型变量的“地址”。就像一个抽屉A,里面什么也不放,只放着另一个抽屉B的钥匙,若想拿到抽屉B中的物品,必须先从抽屉A中取钥匙。
(08分22秒)下面我们来看一个指针的执行情况:
int i=15,j=0; /定义2个普通变量/
int *pi,*pj=0; /这行定义2个指针,pj初始化为空指针/
pi=&i; /用&运算符将i的地址赋给指针pi/
(14分15秒)注意:
※可以将地址0赋给指针,如pi=0时,系统会自动将0转成空地址赋给pi。但是像pi=0x0000FFFF这样的表达式,被视为一种很危险的操作。
(18分34秒)这里用了倒序讲解※等号左边只允许是容器,右边只允许是数值。如果右边是容器,它会自动取出容器里的数值。
(15分47秒)※*在定义时出现表示定义指针,在表达式中出现表示取值。它还有个作用就是做乘号,这时它是个双目运算符。
如:
int i=5,j=0,*pi; /定义指针/
pi=&i;
int *pj=pi; /定义指针/
j=*pi; /取值/
pi=50;
j=i5; /作为乘号/
(23分17秒)提问:
int i;
int pi=&i;
这个是定义变量还是取值?
实际上系统看见“变量定义”与“表达式”合成的语句,会自动分解。如上式会被分解为:
int *pi;
pi=&i;
int *p1=&a,*p2=p1;会被分解为:
int *p1,*p2;
p1=&a;
p2=p1;
(28分22秒)请同学们思考一下:int p=p1;和p=*p1;的含义各是什么?
(29分12秒)程序1
用指针间接操作2个变量(见Excel演示)
(34分40秒)讲解第2个函数
(54分22秒)※指针就相当于存放着各变量的钥匙,得到变量的钥匙用&i,从指针中拿钥匙取值就用*pi
※指针不能指向不同类型的变量。
如:
float f=3.14;
int *pi=&f; /错误/
(55分16秒)※指针在定义时号可以靠着类型,也可以靠着变量,但中间一定要有空格。通常定义函数返回值时靠着类型,定义变量时靠着变量。为什么呢?
如:
int pi,pj;
/此写法容易误认为定义了2个指针,实际上pj是整型变量/
int *p,pj; /立刻就看出第二个不是指针/
(56分46秒)因为函数不可能定义多个返回值,所以号可以靠着类型。
int fun(){…}/表示此函数返回一个整型指针/
int *fun(){…}
/此写法容易误认为定义了指向函数的指针/
(57分33秒)题目讲解:
若有以下程序:
#include <stdio.h>
main()
{ int k=2,m=4,n=6,*pk=&k,*pm=&m,*p;
*(p=&n)= pk(*pm);
printf(“%d\n”,n);
}
程序的输出结果是
A)4 B)6 C)8 D)10
参考答案:C
(64分30秒)技巧:替换法解题,事半功倍
作业
若有定义:int x,*pb;则正确的赋值表达式是
A)pb=&x B)pb=x
C)*pb=&x D)*pb=*x
第26章 位运算
作用
(00分37秒)在小内存时代,经常把一个字节分成好几份来用,以达到节约内存的目的。
现在经常用于网络数据传输,单片机领域……。
(01分59秒)注意:
※在进行任何位运算前,首先应将进任何表达式的结果转换成二进制,算完以后再转回原来的进制即可得到结果。
※不能对浮点数进行位运算。
按位与
(02分43秒)格式:二进制&二进制
如:
int a=3&5;
相当于
00000011
& 00000101
00000001
转成十进制后a的值为1
(03分56秒)记忆方法:在逻辑与运算中,两边的表达式都为真,结果才为真。因此按位与运算时,两个位都为1,结果才为1。
作用:
(05分10秒)※清零:要将某一位清零,可将相应的位设置为0,其它位为1,再使用&运算。
(07分28秒)※取指定位:想取某一位,可将相应的位设置为1,其它位为0,再使用&运算。
按位或
(09分51秒)格式:二进制|二进制
如:
int a=3|5;
相当于1
00000011
| 00000101
00000111
转成十进制后a的值为7
(10分32秒)记忆方法:在逻辑或运算中,两边的表达式有一个为真,结果就为真。因此按位或运算时,有一个位为1,结果就为1。
作用:
(10分56秒)※将某位置1:要将某一位置1,可将相应的位设置为1,再使用 | 运算
按位异或
(13分02秒)格式:二进制^二进制
如:
int a=3^5;
相当于
00000011
^ 00000101
00000110
转成十进制后a的值为6
(14分12秒)记忆方法:“异”就是不同的意思,异或实际上是判断两数是否“不同”,如果不同就为真。因此按位异或时,两数不同,结果为1。
(15分30秒)定理:
1、任何数与自己异或都为0
2、任何数与0异或值不改变
(16分06秒)作用:
※翻转位:要将某一位翻转,可将相应的位置1,第一次用 ^ 运算,可将其翻转,第二次用 ^ 运算,又可将其转回来。早期的鼠标指针用的就是一种图形异或运算,所以速度非常快。
(21分05秒)※不用临时变量交换两个值。如a=4,b=5,可用以下语句实现互换:
a=a^b;
b=b^a;
a=a^b;
用等量代换法分析:a=ab,那么b=ba相当于b=b(ab),去括号得b=bab,移项得b=bba,根据定理1得b=0^a,根据定理2得b=a。
这时候b的值是4,而a只是个中间值。
a=ab相当于a=(ab)(bab),去括号得a=abbab,移项得a=aabbb,根据定理1得a=00^b,根据定理2得a=b。
这时候a的值是5,交换完毕
(26分36秒)程序1
矩阵异或
按位取反
(35分19秒)格式:~二进制
如:int a=~3;
得到11111100,它是个负数。
左移
(36分57秒)格式:二进制<<要移动的位数
如:
int a=3<<2;
相当于00000011<<2得到00001100
转成十进制后,a的值为12
(38分02秒)作用:将二进制数左移一定的位数,最右边补0。任何一位移出左边界,自动丢弃。
※不越界的左移相当于让变量值乘以2。
右移
(41分45秒)格式:二进制>>要移动的位数
如:
int a=12>>2;
相当于00001100>>2得到00000011
转成十进制后,a的值为3
(42分08秒)作用:将二进制数右移一定的位数,最左边补0(或1)。任何一位移出右边界,自动丢弃。
※不越界右移相当于让变量值除以2。
(43分07秒)注意:右移比左移要复杂些,如果要移的数为负数,最左边符号位为1,为了不改变此数的符号位,往右移的时候,左边会自动补1。
(43分52秒)位运算的优先级:~ 高于 << >> 高于 & 高于^ 高于 |
作业
设有定义语句:
char a=3,b=6,c;
则执行赋值语句c=a^b<<2;后变量c中的二进制值是
A)00011011 B)00010100
C)00011100 C)00011000
高级……
(高级)第27章 指针与一维数组
数组与指针
(01分33秒)程序1
指向数组元素的指针(见Excel演示)
(15分23秒)注意:
※指针++并不是指针指向的内容++,而是指针本身++,相当于指针向前移动一个位置。
※指针每次++,并不是移动一个字节,它移动的字节数刚好为指针类型所占的字节数。每次移动多少字节,同学们也可以用sizeof函数自己测出来。
如:
double *pd;
pd++; /pd一次移动了8字节/
(17分38秒)※指针一般在指向数组元素时才使用加减法,指向二维数组时还经常用到乘法
(18分45秒)※指针的比较也就是比较两个指针是否指向同一位置,在指向数组元素的指针中经常用来测试两个指针是否指向同一数组元素。
内存计算
(19分11秒)学了指针以后,大家都知道指针的操作实际上就是对内存的操作。
定义double arr[1000];将占用8*1000=8000=8K的内存,同学们可用sizeof(arr)自己试试。
定义一个指针变量,要消耗4个字节的内存。因此学完这课后,大家就知道为什么买电脑时,内存是越大越好。
指针数组
(20分22秒)格式:
int a[10];
定义变量时,虽然各运算符意义不同,但同样符合C语言的运算符优先级。
在这里a先与[10]结合,表示a是个数组,再与int结合,表示数组的每个元素都是int*型,也就是int型指针。
(22分48秒)程序2
指针数组
指向数组的指针
(29分58秒)格式:
int (a)[10];
a先与结合,表示a是个指针,再与[10]结合,表示它指向有10个元素的数组,再与int结合,表示这个数组是int型。
(33分31秒)程序3
指向数组的指针
(42分36秒)切割记忆法:
a: a先与结合,不管两旁有什么东西,表示a只是“1个”指针,指向什么,以后再说。
a[10]: a先与[10]结合,表示a有“10个”元素,这10个元素是什么类型,以后再说。
数组名与指针
(46分33秒)程序4
数组名与指针
(52分20秒)※数组名实际上是个只读指针,[n]运算早就被C语言的设计者设计好,在电脑中取a[n]的值相当于*(a+n)
请问*(a+n)与a+n有什么区别(见Excel演示)?能否进行a++这样的操作?
(59分36秒)※数组名与指针的差别是:数组名是只读的,而指针可以改写。
假设a是数组名,p是指针,p++就可以,a++就不行。
(60分58秒)※因为a[0]等价于(a+0)等价于*a,所以&a[0]与 a意义相同。因此p=&a[0]和p=a效果一样。
(71分47秒)题目讲解:
以下程序的输出结果是什么?
int *var,ab;
ab=100; var=&ab; ab=*var+10;
printf(“%d\n”,*var);
参考答案:110
++的分解
(73分51秒)假如p是一个赋了值的char型指针,如果遇到如下表达式
char a=*p++;
可分解为:
char a=*p;
p=p+1;
(75分53秒)第二种情况:
char a=*++p;
可分解为
p=p+1;
char a=*p;
(76分54秒)第三种情况:
char a=(*p)++;
可分解为
char a=*p;
(*p)=(*p)+1;
请同学们自己考虑char a=++(*p);的分解。
(78分15秒)题目讲解:
定义int a=5,b;则执行表达式b=++a*–a之后,变量b的值为____
参考答案:25
(81分26秒)PPT演示总复习:前面学的4种指针横向比较。(重点)
作业
1、用指针输出一个数组中的全部元素
2、有以下程序:
#include <stdio.h>
main()
{ int a[]={2,4,6,8,10},y=1,x,p;
p=&a[1];
for(x=0;x<3;x++) y+=(p+x);
printf(“%d\n”,y);
}
程序的输出结果是
A)不确定的值 B)13 C)19 D)21
3、有以下程序:
#include <stdio.h>
main()
{ int a[]={2,4,6,8},*p=a,i;
for(i=0;i<4;i++) a[i]=*p++;
printf(“%d\n”,a[2]);
}
程序的输出结果是
A)6 B)8 C)4 D)2
第28章 文件
文件指针
(03分12秒)定义:
FILE *指针变量名;
如:
FILE *fp1,*fp2;
一次定义了两个指向文件的指针
文件操作方式
(04分47秒)※文本方式
文本方式将数据以ASCII码方式存储在文件中,我们电脑中的“文本文件”就是典型的以文本方式存储的文件。
(05分57秒)※二进制方式
音乐、图形这类非文本数据,就比较适合使用二进制方式存储
打开文件
(06分44秒)格式:
FILE *fopen(文件名,文件使用方式);
如:
FILE *fp;
fp=fopen(“file_a”,”r”);
(09分03秒)使用方式列表:
※ ”r”,文本只读
※ ”rb”,二进制只读
※ ”w”,文本只写
※ ”wb”,二进制只写
※ ”a”,文本添加
※ ”ab”,二进制添加
(11分10秒)※ r+,w+,a+,rb+,wb+,ab+
带加号方式打开的文件,读写都允许,其它操作与前面6项一样。
(14分08秒)打开文件经常会出现错误,比如这个文件根本就不存在。可用以下方法检查:
if((fp=fopen(“file_a”,”r”))= =NULL)
{
printf(“Cannot open this file\n”);
exit(0); /使程序退出运行/
}
关闭文件
(18分05秒)格式:
fclose(文件指针);
文本文件的读写
(19分11秒)写入:
putc(字符,文件指针);
读取:
char getc(文件指针);
(23分13秒)判断是否文件尾部:
EOF
因为ASCII字符没有负数,所以在C语言中将EOF定义为-1。用文件读取函数时,遇见-1或EOF即为文件结束标志。
(24分28秒)程序1
将字符串”Hello”写入文本文件
(36分33秒)程序2
读取程序1中文本文件的内容
(47分55秒)除这两个函数外,还有几个可以批量读取数据的函数:
fscanf(文件指针,格式控制字符串,输出表列);
如:
fscanf(fp,”%d %d”,&a,&b);
fprintf(文件指针, 格式控制字符串,输出表列);
如:
fprintf(fp,”%d %d”,a,b);
(54分59秒)fgets(数组名,字符个数,文件指针);
如:
fgets(str,n,fp);
fputs(数组名,文件指针);
如:
fputs(str,fp);
※这几个函数有点类似我们平时操作字符串时候用到的函数,只是fgets加了个“字符个数”参数。同学们有时间可以自己尝试在程序中使用。
二进制文件的读写
(57分06秒)二进制文件可以存储任何类型的数据,就有可能出现负数,因此在判断文件尾部时得改用其它方法。
(58分18秒)判断文件尾部函数:
int feof(文件指针);
遇到文件结束,返回值为1,否则为0。
(58分50秒)写入:
fwrite(内存首地址,内存大小,内存个数,文件指针);
读取:
fread(内存首地址,内存大小,内存个数,文件指针);
(59分34秒)程序3
读写二进制文件
(67分39秒)第2段程序演示:文本文件与二进制文件的差别
(76分09秒)题目讲解:
1、以下程序用来统计文件中字符的个数。请填空。
#include <stdio.h>
void main()
{ FILE *fp; long num=0;
if((fp=fopen(“fname.dat”,)==NULL)
{ printf(“Open error\n”);exit(0); }
while()
num++;
printf(“num=%ld\n”,num);
fclose(fp);
}
参考答案:(1)”r” (2)getc(fp)!=EOF
(81分39秒)2、有以下程序:
#include <stdio.h>
main()
{
FILE *fp;int k,n,a[6]={1,2,3,4,5,6};
fp=fopen(“d2.dat”,“w”);
fprintf(fp,“%d%d%d\n”,a[0],a[1],a[2]);
fprintf(fp,“%d%d%d\n”,a[3],a[4],a[5]);
fclose(fp);
fp=fopen(“d2.dat”,“r”);
fscanf(fp,“%d%d”,&k,&n);
printf(“%d%d\n”,k,n);
fclose(fp);
}
程序运行后的输出结果是:
A)12 B)14 C)1234 D)123456
参考答案:D
(88分17秒)3、有以下程序:
#include <stdio.h>
main()
{
FILE *fp;int i,a[6]={1,2,3,4,5,6};
fp=fopen(“d3.dat”,“w+b”);
fwrite(a,sizeof(int),6,fp);
/*该语句使读文件的位置指针从文件头向后移动3个int型数据*/
fseek(fp,sizeof(int)*3,SEEK_SET);
fread(a,sizeof(int),3,fp);fclose(fp);
for(i=0;i<6;i++)printf("%d,",a[i]);
1
2
3
4
5
}
程序运行后的输出结果是:
A)4,5,6,4,5,6 B)1,2,3,4,5,6
C)4,5,6,1,2,3 D)6,5,4,3,2,1
参考答案:A
作业
请调用fputs函数,把5个字符串输出到文件中(注意每个字符串用回车分开);再从此文件中读入这5个字符串放在一个字符串数组中;最后把字符串数组中的字符串输出到终端屏幕,以检验所有操作是否正确。
第29章 预处理
宏定义
(02分20秒)格式:
#define 标识符 字符串
如:
(05分03秒)#define PI 3.1415926
以后程序一看见PI,就自动会用3.1415926替代。
注意:
※宏命令只是简单地替换字符串,但是它不做合法性检查。比如将3.1415926写成3.14T5926,只会在使用了PI的语句上报错,而不会在#define这行上报错。
※宏定义后面不得加分号
※宏定义后面的字符串可以省略。
如:
#define PI
此定义的作用较少用到,在这里不作详解。
(07分34秒)※可以直接用宏定义一条语句。
如:
#define PT printf(“%d\n”,a)
以后就可以用PT来代替printf(“%d\n”,a)这条语句。
(08分54秒)※在进行宏定义时,可以引用已定义的宏名,可以层层置换
#define R 3.0
#define PI 3.1415926
#define L 2PIR
#define S PIRR
main()
{
printf(“L=%f\nS=%f\n”,L,S);
}
运行情况如下:
L=18.849556
S=28.274333
(12分11秒)※在字符串中的如果出现与宏名相同的字符串,不进行替换,如上面双引号中的L会原样输出。
※在学习宏命令时,一定要学会将它展开,再参与运算。如上面的printf函数层层展开,最后为:
printf(“L=%f\nS=%f\n”,23.14159263.0,3.14159263.03.0);
(15分07秒)※宏定义和普通变量不同,它不分配内存空间
※宏名称虽然没有规定非用大写,但是在C程序员的书写习惯当中,经常使用大写,以区分它与普通变量和函数。
逻辑值
(16分43秒)程序员可以自己用#define来定义TRUE和FALSE来增加程序的可读性。
(17分25秒)※NULL在系统中已经被定义成#define NULL 0,EOF在系统中已经被定义成#define EOF -1
这样,在判断指针是否为空时,if(pi!=NULL)与if(pi)就没有什么分别。
那么,打开文件是否出错的代码可以缩减为:
if(!(fp=fopen(“file_a”,”r”))){……}
带参数的宏定义
(24分30秒)宏定义也可以加上参数,这时它的功能有点类似函数,但是它和函数有本质上的区别。
格式:
#define 宏名(参数表) 字符串
如:
#define MUL(a,b) ab
c=MUL(3,5);
展开得c=35;
(29分48秒)※也可在参数表中代入复杂点的字符串。如:
c=MUL(2+3,1+2);
展开得:
c=2+31+2;
这行代码出现了问题:号改变了两个参数的优先级,因此,一般带参宏替换的标准定义形式为:
#define c=MUL(a,b) (a)(b)
上面的代码展开得:
c=(2+3)(1+2);
这样展开的结果符合程序员意图。
(32分03秒)※前面说过,还有一种方法可以使用“常量”,那就是用宏替换。
#define NUM 3
main()
{
int a[NUM];
}
用宏替换来定义数组,也是可以的。
什么叫“预处理”
(34分32秒)每个程序在编译前,计算机会预先干一些事:那就是碰到预处理指令时,将它展开,得到真正的代码。如上面的代码经过编译器预处理后,得到的可运行代码实际上是:
main()
{
int a[3];
}
因此如果把#define NUM 3误写成#define NUM 3T,程序会在第3行报错。以此类推,如果在多个地方使用了NUM宏,编译器会在使用过NUM的行数上都报个错。
(36分14秒)请同学们想想,如果误写成#define NUM 3;(这里加了个分号),会出现什么问题?
文件包含
(38分13秒)格式:
#include “文件名”
或
#include <文件名>
双引号一般用来包含用户文件名,尖括号一般用来包含系统自带的文件名。如stdio.h就是系统文件名,所以包含stdio.h时,就要用尖括号括起来。
有了文件包含功能以后,将一个大程序划分成多个文件就成为可能。多个文件可同时让多个人编写,再用#include将其它人写好的文件包含进来即可使用,不用关心它的细节。
(42分10秒)程序1
用多文件模式计算任意两数之间所有整数之和
(53分54秒)程序演示:如何建立头文件
※用#include包含的文件后缀一般是.h
※文件包含也可以嵌套
作业
用多文件模式计算n的阶乘
第30章 自定义类型
typedef自定义类型
(02分28秒)格式:
typedef 新类型名
如:
typedef int INTEGER;
typedef float REAL;
定义之后就可以用新的类型定义变量:
INTEGER i;
(04分25秒)typedef还有更复杂的用法,比如用它去定义一个数组名,再用这个数组名去定义变量:
typedef int ARR[100];
ARR a,b,c;
这个定义相当于int a[100],b[100],c[100];
(06分08秒)实际上,typedef的定义不像想象中这么简单,下面就归纳下它的定义方法:
(1)先按定义变量的方法写出定义体(如:int i)
(2)将变量名换成新类型名(如:将i换成COUNT)
(3)在前面加typedef(如:typedef int COUNT)
(4)然后可以用新类型名去定义变量。
再以定义上述的数组类型为例来说明:
(1)先按定义数组变量形式书写:int n[100];
(2)将变量名n换成自己指定的类型名:int ARR[100];
(3)在前面加上typedef,得到typedef int ARR[100];
(4)用来定义变量ARR a;
(10分02秒)注意:
※typedef只能定义各种类型,但它不能定义变量。
(13分23秒)※C语言没有字符串类型,可以用typedef自己定义一个字符串类型:
typedef char STRING[100];
STRING Str;
Str就是个字符串,比使用char Str[100];来定义字符串要直观的多。
结构与自定义类型
(16分48秒)在这里,我们先进一步学习一下结构定义的两种特殊形式:
第一种:定义结构,同时定义一个结构变量stu,当然也可以同时给它的成员进行初始化。注意这个结构定义时没有结构类型名,因此这个结构只定义了一次变量stu,就再也不起作用了。因为你不知道再用什么方式引用它。
struct
{
…
}stu;
(18分42秒)第二种:定义结构时顺便定义一些变量或同类型数组。
struct student
{
…
}stu, stuarray[5];
这种变量定义可分解为:
struct student stu,stuarray[5];
省掉中间的花括号,就是它的定义形式。
(24分29秒)同样,使用typedef可以将结构后面定义的变量提升成类型:
typedef struct student
{
char name[9];
int age;
}STU,STUARRAY[5];
参照typedef的定义逻辑,以后用STU stu就相当于struct student stu;用STUARRAY stuarray就相当于struct student stuarray[5]。
(29分33秒)程序1
用typedef定义结构类型
作业
将“结构与自定义类型”小节定义的STU和STUARRAY两种类型用到实处(比如从5个学生中找出成绩最好的学生)。
第31章 指针与二维数组
数组的特殊情况
(03分28秒)※数组在定义时可以省略下标:
int ai[]={1,2,3};
编译器会自动算出此数组在初始化时有3个元素,自动定义成int ai[3]={1,2,3};
这种方法经常用在字符串上:
char str[]={“string”};
同学们可以自己算出str的长度。
(04分53秒)※二维数组初始化时,只有第一维下标可以省略:
int ai[][3]={{1,2,3},{4,5,6}};
同学们可以自己算出第一维下标的长度。
(06分20秒)※也可以用一维数组的形式初始化二维数组:
int ai[][3]={1,2,3,4,5,6};
缺胳膊少腿也行:
int ai[][3]={1,2,3,4};
元素ai[1][1]和ai[1][2]会自动初始化为0
(08分53秒)※无论多少维数组,它的内存都连成一片,我们可以用通过指针的移动来指向数组中的任何一个元素。
int ai[][3]={1,2,3,4,5,6};
int *pi=&ai[0][0];
pi+=3;
这时pi指向ai[1][0];
(12分27秒)注意以下写法都是错误的:
※int ai[][3]={{1,2,3}{4,5,6}};
少了中间的逗号
※int ai[][3]={{1,2,3},{}};
不能用空花括号初始化,一维数组也一样
※int ai[0];
不能定义只有0个元素的数组
※int ai[3][]={{1,2,3},{4,5,6}};
除第一维下标外,其它维的下标都不能省略
二维数组与指针
(14分41秒)在学二维数组与指针之前,我们先回顾下一维数组与指针的关系。
a[3]等价于*(a+3)
a[0]等价于*(a+0)等价于*a
(17分02秒)二维数组可当成是“数组的数组”,假设二维数组定义如下:
int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}
a是个数组名,包含3个元素:a[0],a[1],a[2],每个元素又是一个数组,包含4个元素a[0][0],a[0][1],a[0][2],a[0][3]。
(18分28秒)从一维数组的知识得知:a[2]相当于*(a+2),但是它只能取到下一维数组的地址,即&a[2][0]。那么如何取到a[1][2]的内容呢?用如下方法:
((a+1)+2)
它等价于
a[1][2]
(25分51秒)※一般在没有&影响的情况下,n维数组中有n个就会取到数据,小于n个,只能取到地址。
通过以上表达式,我们可以推出另外几个表达式:
a[0][1]等价于*((a+0)+1)等价于(a+1)
a[1][0]等价于((a+1)+0)等价于**(a+1)
a[0][0]等价于(*(a+0)+0)等价于**(a+0)
(31分26秒)题目讲解:
已知数组int x[5][4]={0};中x的地址为0x0AFD0100,求**(x+3)+2、x+3、(*(x+3)+2)的地址或值。
参考答案:2 0x0AFD010C 0
说明:一、**(x+3)+2相当于*((x+3)+0)+2相当于x[3][0]+2=2
(36分48秒)二、x+3 相当于(x+0)+3相当于x[0]+3相当于x[0][3]的地址&x[0][3],等于0x0AFD0100+34等于0x0AFD0100+C=0x0AFD010C。
三、((x+3)+2)相当于x[3][2]=0
(45分54秒)重点程序1
二维数组与指针的关系(见Excel演示)
(79分03秒)讲解程序,前面通过Excel表格讲解原理
多维数组
(87分56秒)多维数组地址换算(见Excel演示)
※内存是线性结构,数组维数再多,也要通过公式转换为线性地址将数据存储在内存中。
作业
已知有二维数组:
int a[3][2]={2,4,6,8,10,12},a的地址为3368,请写出以下算式的地址或值。
1、&a 2、a[2]
3、(a+1)+1 4、((a+1)+1)
5、&a[1] 6、*a+1
7、a+1
第32章 指针与字符串
字符串的输入输出
(00分00秒)printf和scanf
格式控制符为%s /s为string的意思/
与字符型数据处理不同的是,用%s后面的变量一定是数组名或是字符指针。
如:
char *pc=”string!”;
printf(“%s\n”,pc);
字符数组与指针
(02分29秒)定义格式:(见Excel演示)
char *pc=”string!”;
char s[ ]=”string!”;
赋值:
pc=”string!”; /指针可以在运算中赋值/
s=”string!”; /数组不允许/
(09分18秒)注意:
※字符指针在运算过程中可以用上面的方式赋值,但是用scanf或gets这类函数从键盘获取字符串时,一定要令其先指向某个数组。
如:scanf(“%s”,pc),运行时会报错,
(13分36秒)※可以通过移动指针指向串中某个字符,如:
printf(“%c”,*(pc+2));
会输出r
※双引号也是一种运算符,它会求出一串字符的首地址。上面的”string!”相当于用&求出’s’的地址赋值给pc。
※字符数组(字符串)初始化,系统会自动按顺序填充数组元素。平时运行时不能用=号给数组赋值,因为数组名是只读的。
字符串数组与指针
(16分09秒)所有的字符串都是一维数组,那“字符串数组”肯定是二维数组。
字符串之间的运算都要通过字符串函数,而不能用简单的>、<、=这样的运算符,这是初学者易犯的错误。
(18分10秒)程序1
用前面学过的选择排序法给5个字符串从小到大排序(见Excel演示)
(21分06秒)讲解程序,前面通过Excel表格讲解原理
(26分08秒)字符串比较规则:
比较的时候,从字符串左边开始,依次比较每个字符,直到出现差异、或者其中一个串结束为止。
比如ABC与ACDE比较,第一个字符相同,继续比较第二个字符,由于C的ASCII码比B大,所以不再继续比较,结果就是后面一个串大。
再如ABC与ABC123比较,比较三个字符后第一个串结束,所以就是后面一个串大。
所以,长度不能直接决定大小,字符串的大小是由左边开始第一个差异字符决定的。
(29分48秒)题目讲解:
阅读下列程序,则在执行后,程序的运行结果为
#include <stdio.h>
#include <string.h>
main()
{ char a[30]=“nice to meet you!”;
strcpy(a+strlen(a)/2,“you”);
printf(“%s\n”,a);
}
A、nice to meet you you B、nice to
C、meet you you D、nice to you
参考答案:D
(34分53秒)下面会出错的程序是:
A、char *pc=“string”; B、char *pc;
pc=“string”;
C、char *pc=”string”; D、char *pc;
printf(“%s”,pc); scanf(“%s”,pc);
参考答案:D
(39分00秒)下面会出错的程序是:
A、char s[ ]=“string”; B、char s[10];
s=“string”;
C、char s[10]=”string”; D、char s[10];
printf(“%s”,s); scanf(“%s”,s);
参考答案:B
作业
1、请手工比较下列字符串大小,再上机用strcmp函数验证:
1)“1A3C”与”1A2” 2)“CBA”与”CBA5”
3)“Ab35”与”AB350” 4)”XYZ”与”X2C”
2、尝试自己写出将5个字符串从小到大排序的程序(先要写出大概思路)
第33章 指针与函数
传值与传址
(00分30秒)父函数如果将变量的钥匙复制一份,再传给子函数,子函数就可以用钥匙开启这个变量,来进行操作。C语言中这种做法叫“传递指针”。
(01分24秒)程序1
传址演示:设计一个函数,它可以交换任意两个变量的值。(见Excel演示)
(07分03秒)通过Excel讲解原理
(11分10秒)间接传递指针演示
(20分47秒)用替换法简化程序分析时间
请同学们思考一下:
(25分23秒)※如果不用指针能否交换这两个变量?
(27分56秒)※swap函数改成如下代码,能否交换两个变量?
void swap(int *i1,int *i2)
{ int *temp;
temp=i1;i1=i2;i2=temp;
}
scanf与&
(33分27秒)假设scanf函数定义如下:
void scanf(int *p1,int *p2)
{
*p1=从键盘读值;
*p2=从健盘读值;
}
main()
{
int a,b;
scanf(&a,&b);
printf(“%d,%d”,a,b);
}
(36分54秒)变化1:改成不用指针
(37分55秒)变化2:printf为什么不用加&
void printf(int a,int b)
{
屏幕显示=a;
屏幕显示=b;
}
main()
{
int a,b;
printf(a,b);
}
数组名做函数参数
(39分44秒)前面我们简单学习了数组名做函数参数,现在我们进一步研究它。
(40分28秒)程序2
数组名做函数参数
(46分01秒)注意:
※父子函数传递数组时,人们最先想到是使用这样的方式传递:
fun(b[n]){…}
main()
{
int a[10];
fun(a[10]);
}
这是错误的理解。
(47分42秒)※C语言规定:在函数的形式参数中,一旦发现int arr[]或int arr[3]这种数据类型定义,就统一编译为int *arr
因此,向子函数传递整个数组时,一定要附加一个参数来传递数组元素个数int n,定义成int arr[3]没有用,父函数照样可以传递一个int a[100]或是int a[2]进去。
(52分24秒)※用数组名做参数有2个巨大的好处:
(1)再大的数组,只要传递2个参数,大大提高编程效率
(2)子函数不用返回值也可以批量修改父函数中的数据,真正做到“内存共享”。子函数相当于不用return,就可以返回值给父函数,还同时返回多个值。
因此,在函数间传递数组名,是经常使用的方法。
函数指针和函数返回指针
(56分25秒)函数指针和函数返回指针考试出现的概率很小,我只用一个程序做讲解,大家重点是是要分清楚这两种定义的区别:
函数指针(也叫指向函数的指针):
int (*pf)(int a); /注意分号/
函数返回指针(也叫返回地址的函数),它的定义形式为:
int* fun(int a){…}
(64分30秒)程序3
用函数找出一个数组中的最大数,返回它的地址
类型分解
(78分27秒)※类型定义符出现在不同的标识符前,所表达的含义不同:
int i
出现在变量前,表示定义整型变量
int fun()
出现在函数前,表示函数返回int
int a[10]
出现在数组前,表示数组的每个元素都是整型
int *p
出现在指针前,表示定义的指针是整型
(79分44秒)※变量之间通过指针或结构赋值或函数间传递参数时,类型必须相同,否则编译无法通过。
像int a;这样的类型定义,较好理解,表示a为int型,若是遇到复杂的类型,我们可以先找到变量或函数在程序头部的定义,将它的名称删除,就是它的类型。
(81分10秒)下面几个是容易混淆的定义:
int* fun() 类型为int* ()
int a[10] 类型为int[10]
int (pf)() 类型为int()()
int (b)[10] 类型为int()[10 ]
(83分50秒)题目讲解:
下列程序是用来判断数组中特定元素的位置所在的。
#include <conio.h>
#include <stdio.h>
int fun(int *s,int t,int *j)
{ int i;
*j=0;
for(i=0;i<t;i++)
if(s[*j]<s[i])*j=i;
return s[*j];}
main()
{int a[10]={876,675,896,101,301,401,980,431,451,777},k;
fun(a,10,&k);
printf(“%d,%d\n”,k,a[k]);}
如果输入如下整数:
876,675,896,101,301,401,980,431,451,777
则输出结果为:
A)7,431 B)6 C)980 D)6,980
参考答案:D
作业
1、请编写函数,对传送过来的三个数选出最大和最小数,并通过形参传回调用函数。
2、请编写函数,其功能是对传送过来的两个浮点数求出和值与差值,并通过形参传送回调用函数。
第34章 指针与结构
结构与函数
(01分22秒)程序1
结构变量与函数
(11分50秒)※与普通变量一样,使用结构变量当形式参数时,只能是单相传递。
它被系统分解为:
struct student stu;
s=stu;
试问,在这里你改变s的值,是否会改变stu的值?
(13分45秒)※函数同样可以返回结构变量,返回形式与普通变量类似。
※无论是返回值还是传递参数,使用结构变量做函数参数,数据都会大量复制。比如student定义了50个成员,上面小小的s=stu这个表达式,就使系统自动进行了如下动作:
strcpy(stu.name,s.name);
strcpy(stu.sex,s.sex);
stu.age=s.age;
…
直到把这50个成员复制完毕。
这大大耗费了CPU和内存资源,因此,在传送结构时,应多使用结构变量的指针。
指向结构变量的指针
(15分10秒)※指针用的最多,并且可以显著提高效率的一个用法,就是在函数间传送结构变量。
(18分01秒)※使用一个“指向结构变量指针”的成员变量有所改变,将前面的程序代码修改一下:
struct student stu; /定义一个结构变量/
struct student *pstu;/定义一个相同类型的结构变量指针/
※成员变量和普通变量一样可以用&号取地址。
pstu=&stu; /给指针赋值/
(pstu).age=18; /给结构成员变量赋值/
pstu->age=18; /括号和号可以直接用->(减号+大于号)运行符替代/
(24分17秒)程序2
使用指针传送结构变量
(29分29秒)※传递结构变量的指针与直接传送结构变量各有好处。使用指针传递结构变量的好处显而易见,父函数与子函数之间只要传递一个4字节的指针即可随意操纵父函数的结构变量。缺点就是:如果好几个函数都使用指针方式,这些函数都“共享”这个结构变量,都给它赋值的话,最后不知道这个结构变量会改成什么样子。
typedef的实用价值
(31分37秒)运用“自定义类型”一章学过的知识,在这里我们将typedef与结构指针联系起来:
struct student
{
…
}stu,*pstu;
这种变量定义可分解为:
struct student stu,*pstu;
省掉中间的花括号,就是它的定义形式。
(33分22秒)同样,使用typedef可以将结构后面定义的变量提升成类型:
typedef struct student
{
char name[9];
int age;
}STU,*PSTU;
参照typedef的定义逻辑,以后用STU stu就相当于struct student stu;用PSTU pstu就相当于struct student *pstu。
(36分52秒)程序3
用typedef定义结构指针
作业
有以下程序:
#include <stdio.h>
struct st
{ int x;
int *y;
}*p;
main()
{
int dt[4]={10,20,30,40};
struct st aa[4]={50,&dt[0],60,
&dt[0],60,&dt[0],60,&dt[0]};
p=aa; printf(“%d\n”,++p->x);
printf(“%d\n”,(++p)->x);
printf(“%d\n”,++(*p->y));
}
程序的运行结果是
A)10 B)50 C)51 D)60
20 60 60 70
20 21 11 31
第35章 双重指针与数组
多维数组与指针
(00分00秒)回顾以前学过指向一维数组的指针,定义格式为:
int *pa;
int a[5];
pa=a;
(04分48秒)这里用了倒序讲解指向三维数组的指针,定义格式为:
int (*pa)[5][8]
int a[3][5][8];
pa=a;
从定义格式中我们看出,指向多维数组的指针,只有将第一维变成指针,后面的格式保持不变才能进行赋值。
(02分21秒)由此得出指向二维数组的指针定义格式为:
int (*pa)[8];
int a[3][8];
pa=a;
(06分54秒)※按照以上规律,如果函数之间要传送一个多维数组,可用下面三种定义:
void fun ( int (*pa)[5][8],int n){…}
void fun ( int a[][5][8],int n){…}
void fun ( int a[3][5][8],int n){…}
再用以下方式调用:
int a[3][5][8]={0};
fun(a,3);
这样理解可以简化成按一维数组的传送方式进行传送。
(10分35秒)题目讲解:
若有定义:int c[4][5], (cp)[5];和语句cp=c; ,则能正确引用c数组元素的是
A)cp+1 B)(cp+3)
C)(cp+1)+3 D)(*cp+2)
参考答案:D
指针的指针
(15分46秒)每个变量都有地址,包括指针变量,如果要存储指针变量的地址,那只能定义一个指针的指针,也叫双重指针,如:
int **ppa,*pa;
ppa=&pa;
双重指针一般和指针数组配合使用。
(19分02秒)程序1
双重指针
(29分49秒)※char p={“aa”,”bbb”,”cccc”}非法,而char *ca[]={“aa”,“bbb”,“cccc”}合法。因为p只能指向一个指针,而ca[]可以为字符串分配内存空间,它们道不同不相为谋。
(30分38秒)注意:
※指针的指针不能直接指向多维数组,如:
int **ppa,a[3][3];
ppa=a;
这是不允许的
因为有数组的存在,双重指针使用的机会不是很多,多重指针的使用更是罕见,所以同学们在学习双重指针概念时不必深究,只要记住“双重指针不能定义字符串,不能指向二维数组”这两句话即可。
(33分50秒)题目讲解:
若有定义:int **ppa, *pa,a[3],b[2][3]; ,以下语句正确的是
A)pa=b; B)ppa=b;
C)pa=”abc”; D)ppa=&pa;
参考答案:D
main函数参数
(35分57秒)Windows中有个“命令行模式”,源自于它的曾曾曾祖父操作系统“DOS”,在没有鼠标的情况下,它可以通过给计算机发文字指令的方式来完成操作系统的大部分工作。
(41分22秒)比如copy a.txt b.txt命令,会将文件a.txt复制到文件b.txt中去。(见命令行演示)
(45分30秒)现在我们就来研究Windows命令是怎么做出来的。
main函数还有一种格式:
main(int argc,char *argv[])
根据“多维数组与指针”的概念,实际上main函数接收的就是一个字符串数组。int argc负责接收字符串的个数,char *argv[]负责接收若干字符串。
(48分31秒)程序2
带参数的main函数
(57分23秒)正式讲解程序2,前面用程序1过度
(58分32秒)程序3
脑筋急转弯
(64分59秒)※字符串数组下标从0开始记数,文件名本身也算1个参数,如:copy a.txt b.txt中的copy也算1个参数,它的下标是0。
※中文和英文的编码格式不同,因此在编程时为减少错误的发生,尽量少用中文。
作业
1、假定以下程序经编译和连接后生成可执行文件PROG.EXE,如果在DOS提示符下键入PROG ABCD EFGH IJKL(此处代表Enter健),则输出结果为_________。
#include <stdio.h>
main(int argc,char *argv[])
{ while(–argc>0)printf(“%s”,argv[argc]);
printf(“\n”);
}
2、有以下程序:
#include <stdio.h>
main()
{ int aa[3][3]={{2},{4},{6}},i,*p=&aa[0][0];
for(i=0;i<2;i++)
{ if(i= =0)aa[i][i+1]=*p+1;
else ++p;
printf(“%d”,*p);
}
printf(“\n”);
}
程序的输出结果是
A)23 B)26 C)33 D)36
第36章 链表理论
动态内存分配
(00分00秒)程序1
sizeof与结构
(07分58秒)※在sizeof中使用变量和类型效果一样,如果计算的是变量,它只会计算出变量对应类型所占的字节数。
(10分04秒)普通的变量定义,在定义时分配内存,函数结束时释放内存,这一切都由系统自动完成。在C语言中,程序员也可以自由地,不受系统控制地手动分配内存。若要手动分配内存,需要用到以下知识点:
(12分16秒)※使用sizeof函数计算变量或类型的大小
※使用malloc函数、free函数分配内存
※使用指针指向已分配的内存。
(13分17秒)程序2
无名变量
(23分48秒)有程序示范,很有趣※malloc和free通常配对使用,malloc的内存如果没有用free释放,会造成“内存泄露”。
链表原理
(28分51秒)使用数组存储数据时,所有的数据在内存中连成一片,需要取出某个数据,只要报上它在内存中的房间编号(数组下标)即可马上取出。
数组也有缺点,它必须预先分配好一些内存,如果这些内存未全部使用,其它的就浪废了。这在字符串的使用中尤为明显:
char str[100]=”hello!”;
程序员定义这个字符串最大能接受99个字符。但是在这里因为实际需求只用了前面7个字符,后面的93个字节就浪废了,浪废率达到90%。
(31分55秒)于是软件工程师们就想出“链表”这种存储方法,它的内存结构如下:
(33分08秒)在C语言中,一般用结构表示链表:
struct student
{
char name[9]; /姓名/
int age; /年龄/
struct student *pNext; /下一结点/
};
(34分16秒)※老师带小朋友游玩时,老师用手拉着第一个小朋友,第一个小朋友又拉着后面的小朋友,以此类推……
这里的“头指针”可以比作老师的手,后面的“指针”就像小朋友的手,最后一个小朋友后面没有拉人,所以它设置为NULL。
链表这种结构,就像锁链一样,一环扣着一环,有一个环坏了,后面所有的结点都会丢失,这就是链表的特点。
(36分02秒)程序3
手拉手的小朋友(见Excel演示)
名词解释
(45分30秒)结点(Node):
在《数据结构》中,结点通常表示一个数据单位,比如在数组中,数组元素可称为结点。在“树”结构中,每个分支元素也称为结点。如:设系为根结点,班是系的子结点,学生是班的子结点。反推过去,系是班的父结点,班是学生的父结点,系是所有子结点的祖宗结点。
链表和数组都不是用来描述父子关系的,它们通常用来描述“兄弟关系”,所以结点与结点的关系不像“树”结构这么复杂。
《数据结构》也可称作数据关系学,有点类似人类的人际关系学,在本课,我们只研究如何用“链表”来表达数据之间的“兄弟关系”。
作业
有以下程序:
#include <stdio.h>
fut(int **s,int p[2][3])
{
**s=p[1][1];
}
main()
{ int a[2][3]={1,3,5,7,9,11},p;
p=(int)malloc(sizeof(int));
fut(&p,a);printf(“%d\n”,*p);
}
程序的运行结果是
A)1 B)7 C)9 D)11
第37章 链表实践
结构变量地址与成员地址
(00分00秒)下列代码:
struct student
{
char name[9]; /姓名/
int age; /年龄/
struct student *next; /下一结点指针/
};
main()
{
struct student stu,*pstu;
int *pi;
pstu=&stu;
pi=&stu.age;
pi=&pstu->age;
}
(05分28秒)pstu只能指向同类型的结构变量地址,pi只能指向同类型的结构成员地址。
创建链表并添加结点
(06分54秒)链表的代码都比较长,要做到无限制地动态创建,有些难度。在这里,我们演示一个链表,并给它简单地创建4个节点,先删除一些需要判断的因素。(见Excel演示)
(30分46秒)这里用了倒序讲解,先用后面的程序1实践,再讲解流程。链表创建流程:
(1)创建一个链表需要3个指针,头指针(pHead),尾指针(pEnd),新指针(pNew)
(2)刚开始链表中一个结点都没有,可用malloc申请内存,动态创建1个结点,令pNew指向它
(3)输入数据后,目前这个新结点又是头结点,也是最后一个结点,所以要令pHead和pEnd也指向它。第一个结点通常需要特殊处理,所以将它放在循环外面建立
(4)剩下3个结点有相同规律,可用(5)到(7)步的循环进行处理:
(5)新建结点给pNew,输入数据
(6)使最后一个结点pEnd的下一结点next指向这个新结点,形成兄弟关系
(7)将新结点pNew设为最后一个结点pEnd
(8)循环结束
(9)不再添加结点,要使最后一个结点pEnd的指针指向NULL
(10)返回头指针pHead
(09分07秒)程序1:create函数
创建链表并输入4个结点
输出链表
(34分18秒)相对其它操作,输出链表结点的操作比较简单,用一个临时指针通过循环不停地指向下一结点即可。
(36分44秒)程序1:print函数
输出链表所有结点
删除结点
(42分50秒)删除结点时通常会进行结点查找,找到符合条件的结点再进行删除操作。
在本程序中找到第3个结点为要删除的结点,再令第2个结点的下一结点指针指向第4个结点,第3个结点就完成了自然脱链动作。
(44分39秒)程序1:del函数
删除指定姓名的结点
插入结点
(54分28秒)插入的意思是把结点插入至链表中间,插入方法有“前插”和“后插”法,前插就是找到目标结点后,在它前面插入新结点,后插就是在其后面插入。本程序中演示的是前插法。有个例外的是:如果未找到指定结点,会将新结点添加到链表最后。
(57分23秒)程序1:insert函数
插入一个新结点在指定姓名之前
(73分40秒)考试要点
作业
以下程序中函数fun的功能是:构成一个如图所示的带头结点的单向链表,在结点的数据域中放入了具有两个字符的字符串。函数disp的功能是显示输出该单链表中所有结点中的字符串。请填空完成函数disp。
#include <stdio.h>
typedef struct node /链表结点结构/
{char sub[3];
struct node *next;
}Node;
Node fun(char s) /建立链表/
{…}
void disp(Node *h)
{ Node *p;
p=h->next;
while(_______)
{printf(“%s\n”,p->sub);p=________;}
}
main()
{Node *hd;
hd=fun();disp(hd);printf(“\n”);
}
第38章 变量生存期
变量的生老病死
所有变量都占着内存,变量什么时候开始占着内存,什么时候从内存中消失,这叫做变量的生存周期。
按照变量的生存周期分类有:
※自动变量auto
※静态变量static
※寄存器变量register
※外部变量extern
auto和static变量是本课讨论的重点,其它类型稍作了解。
函数也有外部函数extern和静态函数static两种,在这里不做探讨。
变量作用域
有时候变量还未消亡,你却不能使用它,这就是变量作用域在作怪。
按照变量作用域分局部变量和全局变量。它们没有什么关键字修饰,只是看你在什么部位定义变量。
下面我们将从变量生存期和作用域来分析几种不同种类变量的特性。
局部变量
定义局部变量的关键字为auto,也叫自动变量。在前面所有课程中,我们定义的变量实际上都是局部变量,因为C语言规定自动变量在定义时前面的auto可以省略。
如:int a;等价于auto int a;
全局变量
在函数外面定义的变量都是全局变量,不管它用什么关键字修饰。
全局变量的特点:
※全局变量的生存期和作用域在从它的定义点开始,一直到本文件的程序结束前,都是有效的,程序结束后消亡,释放内存。
※全局变量作用域中的所有函数将共享这个全局变量中的内存数据,各函数之间也可以通过全局变量传送数据,但在职业程序员中,这是种很不好的习惯。
注意:在同一作用域中,如果局部变量与全局变量重名,全局变量将暂时退居幕后,这时只能操作局部变量。
程序1
局部变量和全局变量(见下图)
讲解下面图形
静态变量
静态变量在程序执行点离开它的作用域时,它并不消亡,它还会死霸着那片内存不放,等着你别的函数下次来使用它。与全局变量不同的是:静态变量通常在函数内部定义,而全局变量只能在函数外部定义。它们的共同点是:在程序结束时,它们都会消亡。
静态变量演示
寄存器变量
寄存器变量不放在内存中,直接放在CPU寄存器中。因为CPU中的寄存器数量有限,所以寄存器变量不能设置太多。
寄存器变量的特点是:比普通内存速度快上百倍。
外部变量
如果一个文件要使用其它文件中的全局变量,就要使用extern声明,这样它就可以和其它文件共享此变量。
作业
以下程序运行后的输出结果是______。
#include <stdio.h>
void fun()
{ static int a;
a+=2;printf(“%d”,a);
}
main()
{ int cc;
for(cc=1;cc<=4;cc++)fun();
printf(“\n”);
}
第39章 常用算法
闰年的判定
闰年的口诀为:四年一闰,百年不闰,四百年又闰。下面这个表达式如果返回1,表示year是闰年。
year % 4 = =0 && year % 100 !=0 || year %400= =0
上式可简化为:
!(i%4) && i%100 || !(i%400)
下面这个表达式如果返回1,表示year不是闰年。
year % 4 != 0 || year % 100 = =0 && year %400 !=0
素数的判定
求n是不是素数,只要求n 除以2~内的所有整数都不能除尽,即为素数。
程序1
素数的判定(非标志法)
数的各位数表示方法
设n=84327,要取出百位数3的算法为:
(1)用84327除以100,得到843
(2)用843%10得到3
取出千位数4的算法为:
(1)用84327除以1000,得到84
(2)用84%10得到4
总结:
从个位数开始数,如果要取出整数s的第n位放入另一个变量k中,公式为
k=s/(10 n-1) %10
那么,84327取千位的写法为:
int s=84327,k;
k=s/1000%10;
数值转字符串算法
(33分18秒)先说明一下数字与ASCII码之间的关系:
‘0’的ASCII值是48 ,那么‘1’的ASCII值是49。也就是说,’1’与1的差值是48,
按这个规律,我们推导出:任何小于10的整数与它的ASCII码的差值是48:
int n=’3’-48;
这时n的结果为3。
(36分17秒)程序2
数值转字符串(见Excel演示)
(55分45秒)程序3
字符串转数值(见Excel演示)
字母大小写转换法
(02分52秒)小写字母比大写字母的ASCII码要大32,因此:
char c=’a’-32;
这时c的结果为’A’。
四舍五入算法
(63分36秒)如果要截取浮点数的第几位小数,计算机不会自动四舍五入,我们可以用下面的算法。
(64分37秒)设f=38.6453,四舍五入到小数点第二位的算法为:
(1)用38.6453乘以100,得到3864.53
(2)加上0.5,得到3865.03
(3)用(int)取整得3865
(4)用3865再除以100.0得38.65
四舍五入到小数点第三位的算法为:
(1)用38.6453乘以1000,得到38645.3
(2)加上0.5,得到38645.8
(3)用(int)取整得38645
(4)用38645再除以1000.0得38.645
(67分07秒)总结:
从小数点第一位开始数,如果要将浮点数f四舍五入到小数点后第n位放入另一个变量k中,公式为
k=(int)(f10n+0.5) /(double)(10n)
那么,38.6453四舍五入到小数点后第三位的写法为:
float f=38.6453,k;
k=(int)(f1000+0.5) /1000.0;
冒泡排序法
(68分06秒)在《数组》的学习当中,我们已经学习了选择排序法。在这节课,我们再学习一下排序的另一种方法:冒泡排序法。
(68分40秒)解题思路:
(1)从第一个数开始,一直到最后一个数,将相邻两个数进行比较,如果第一个数比第二个数小,就交换这两个数
(2)第一轮比较结束,最小的数会“沉”到最底,也就是数组的最右边。
(3)使用(1)的方法比较第一个数到倒数第二个数,将第二小的数沉底。
(4)如果有n个数,从第1个数与n-j(2~n)个数两两进行比较,比较n-1轮,即可将小数一个一个“沉底”,相对小数,大数就好像在往上冒,这就是“冒泡排序法”。
(70分29秒)用5个数来描述以上过程:
(1)57428:57比较得:
(2)75428:54比较,不动,42比较,不动,2和8比较得:
(3)75482:第一轮比较完毕,最小数已在最后一位。第二轮开始:75比较,不动,54比较,不动,48比较得:
(4)75842:第二轮比较完毕,第二小数已在倒数第二位。第三轮开始:75比较,不动,58比较得:
(5)78542:第三轮比较完毕,第三小数已在倒数第三位。第四轮开始:78比较得:
(6)87542:比较完毕,排序结果已经出来。
(71分45秒)程序4
冒泡排序法(见PS演示)
第40章 难题讲解
难题分类(见Excel演示)
(01分27秒)
一、链表类
二、自己编写strcpy、strcat、strlen、strcmp函数
三、矩阵
四、删除指定字符
五、删除若干*号
六、从已排序的数组中删除重复元素
七、平移字符串
一、链表类
(03分54秒)链表程序一般分5个小部分:创建、删除、修改、搜索、打印结点。这些程序篇幅都比较长,有些题目会略去其中的某些部分来缩短程序长度。
在链表程序中比较喜欢做文章的部分,一个是循环判断条件p->next!=NULL,表示判断这个结点的下个结点是否为空,为空表示到了最后一个结点。
另一个是“前往下一个结点”的p=p->next,表示把下一结点的指针赋给p,令p永远指向最新的结点。
想要熟练做出链表的题目,必须得花上一两天时间研究并自己写出链表的各种操作程序,否则就算题目做对,也会在题目的理解上花上大量时间。
(07分48秒)题目:
上机改错题:下列给定程序是建立一个带头结点的单向链表,并用随机函数为各结点赋值。函数fun的功能是将单向链表结点(不包括头结点)数据域为偶数的值累加起来,并且作为函数值返回。
请改正函数fun中的错误,使它能得出正确的结果。
注意:不要改动main函数,不得增行或删行,也不得更改程序的结构。
试题程序:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
typedef struct aa
{ int data;
struct aa *next;
} NODE;
int fun (NODE h)
{ int sum=0;
NODE p;
p=h->next;
/found/
while(p->next)
{ if(p->data%2==0)
sum+=p->data;
/found/
p=h->next;
}
return sum;
}
NODE *creatlink(int n)
{
NODE *h,p,s;
int i;
h=p=(NODE)malloc(sizeof(NODE));
for(i=1;i<n;i++)
{
s=(NODE)malloc(sizeof(NODE));
s->data=rand()%16;
s->next=p->next;
p->next=s;
p=p->next;
}
p->next=NULL;
return h;
}
outlink(NODE *h)
{ NODE *p;
p=h->next;
printf(“\n\n The LIST :\n\n HEAD”);
while§
{ printf(“->%d”,p->data);
p=p->next;}
printf(“\n”);
}
void main()
{ NODE *head; int sum;
system(“CLS”);
head=creatlink(10);
outlink(head);
sum=fun(head);
printf(“\nSUM=%d”,sum);
}
参考答案:
(1)错误:while (p->next) 正确:while (p!=NULL)
(2)错误:p=h->next; 正确:p=p->next;
二、自己编写strcpy、strcat、strlen、strcmp函数(16分16秒)串复制strcpy
/s为目标串,t为源串/
void fun(char *s,char *t)
{
while(*t!=‘\0’)
{
*s++=*t;
t++;
}
*s=‘\0’;
}
(21分13秒)简单写法
void fun(char *s,const char *t)
{
while ((*s++=*t++)!=‘\0’);
}
(25分21秒)※串连接
/s为目标串,t为源串,请注意s的长度要大于两串之和/
void fun(char *s,char *t)
{
while(*s!=‘\0’)
s++;
while(*t!=‘\0’)
{
*s=*t;
s++;
t++;
}
s=‘\0’;
}
(27分50秒)简单写法:
void fun(char *s,char *t)
{
while(*s!=‘\0’)s++;
while((*s++=*t++)!=‘\0’);
}
(28分28秒)※串比较
/s比t小,就返回-1,相同就返回0,大就返回1/
int fun(char *s,char *t)
{
int i=0;
while(*s-*t= =0 && *t!=‘\0’)
++s,++t;
i=*s-*t;
if (i<0)
i=-1 ;
else if (i>0)
i=1;
return i;
1
2
3
4
5
}
(35分36秒)※求长度
/返回值为长度/
int fun(char *s)
{
char *t=s;
while(*t++!=‘\0’);
return t-s-1;
}
三、矩阵
(38分31秒)矩阵中的运算通常是两层循环加二维数组。矩阵题干的难度在于找出两个下标的规律,再用循环实现之。
如:如将一个矩阵行列互换。
矩阵a:
转换成:
矩阵b:
编程题:
编写程序,实现矩阵(3行、3列)的转置(即行列互换)。
例如,若输入下面的矩阵:
100 200 300
400 500 600
700 800 900
则程序输出:
100 400 700
200 500 800
300 600 900
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。
试题程序:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void fun (int array[3][3])
{
}
void main()
{
FILE wf;
int i,j;
int array [3][3]={{100,200,300},{400,500,600},{700,800,900}};
system(“CLS”);
for (i=0;i<3;i++)
{for (j=0;j<3;j++)
printf("%7d “,array[i][j]);
printf(”\n ");
}
fun(array);
printf("Converted array:\n “);
for (i=0;i<3;i++)
{ for (j=0;j<3;j++)
printf(”%7d “,array[i][j]);
printf(”\n ");
}
//
wf=fopen(“out.dat”,“w”);
for (i=0;i<3;i++)
{ for (j=0;j<3;j++)
fprintf(wf,“%7d “,array[i][j]);
fprintf(wf,”\n”);
}
fclose(wf);
//
}
(41分03秒)本题有个笨方法:
void fun (int array[3][3])
{
int b[3][3],i,j,temp;/新数组,两层循环变量,交换用的临时变量/
/将矩阵array置换给矩阵b/
for(i=0;i<3;i++)
for(j=0;j<3;j++)
b[j][i]=array[i][j];
/将矩阵b存入矩阵array/
for(i=0;i<3;i++)
for(j=0;j<3;j++)
array[i][j]=b[i][j];
}
参考答案:
(45分21秒)void fun (int array[3][3])
{
int i,j,t;
/将右上三角和左下三角对换,实现行列互换/
for(i=0;i<3;i++)
for(j=i+1;j<3;j++)
{
t=array[i][j];
array[i][j]=array[j][i];
array[j][i]=t;
}
}
(51分00秒)这个参考答案中其实隐藏了另外两种矩阵题型的解法:
将矩阵的左下半三角的元素都乘以n:
#define N 3
fun(int a[][N],int n)
{
int i,j;
for(i=0;i<N;i++)
for(j=0;j<=i;j++)
a[i][j]=a[i][j]*n;
}
(52分13秒)将矩阵的右上半三角的元素都乘以n:
void fun(int a[ ][N], int n)
{
int i,j;
for(i=0;i<N;i++)
for(j=i;j<N;j++)
a[i][j]=a[i][j]*n;
}
四、删除指定字符
(53分06秒)编程题
编写函数fun,该函数的功能是:从字符串中删除指定的字符。同字母的大、小写按不同字符处理。
例如,若程序执行时输入字符串为:
turbo c and borland c++
从键盘上输入字符n,则输出为:
turbo c ad borlad c++
如果输入的字符在字符串中不存在,则字符串照原样输出。
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。
试题程序:
#include <string.h>
#include <stdio.h>
void fun( char s[],int c)
{
}
main()
{
static char str[]=“turbo c and borland c++”;
char ch;
FILE out;
printf (“The original data is :%s\n “,str);
printf(“input a string:\n”);
scanf(”%c”,&ch);
fun(str,ch);
printf(“str[]=%s\n”,str);
strcpy(str,“turbo c and borland c++”);
fun(str,‘a’);
//
out=fopen(“out.dat”,“w”);
fprintf(out,“%s”,str);
fclose(out);
/*/
}
(54分12秒)参考答案:
void fun(char s[], int c)
{
int i=0;
char *p;
p=s;
while(*p) /判断是否为结束符/
{
if(*p!=c) /判断字符串中字符是否与指定字符相同/
{
s[i]=*p; /不同将重新组合字符串/
i++;
} /相同则处理下一个字符/
p++;
}
s[i]=‘\0’;
}
(63分00秒)总结:
删除指定字符在编程题中出现的概率非常高,在这里总结下它的流程:
(1)它会利用两个指针或数组元素(原指针和巡检指针)向前移动,一直到巡检指针为\0,并不停地赋值(这题为s[i]=p)
(2)利用一个指针来与要删除的字符比较(这题用p跟c比)
(3)比较到有要删除的字符,不给它赋值,并使原指针停在这个位置。(这题没有执行if中的语句)
(4)记住一定要给最后一个元素补上\0。(这题是s[i]=’\0’)
五、删除若干号
(65分38秒)编程题
假定输入的字符串中只包含字母和号。请编写函数fun,它的功能是:除了字符串前导和尾部的号之外,将串中其他号全部删除。形参h已指向字符串中第一个字母,形参p已指向字符串中最后一个字母。在编写函数时,不得使用C语言提供的字符串函数。
例如,若字符串中的内容为ABCDEFG*****,删除后,字符串中的内容应当是ABCDEFG**。
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。
试题程序:
#include <stdio.h>
#include <conio.h>
#include <string.h>
void fun(char *a,char *h,char *p)
{
}
main ()
{
char s[81],*t,*f;
FILE *out;
printf(“Entre a string:\n”);
gets(s);
t=f=s;
while(*t)
t++;
t–;
while(t=='‘)
t–;
while(f=='’)
f++;
fun(s,f,t);
printf(“The sting after deleted:\n”);
puts(s);
//
out=fopen(“out.dat”,“w”);
strcpy(s,"ABCDEFG");
fun(s,s+4,s+13);
fprintf(out,“%s”,s);
fclose(out);
/*******/
}
(66分58秒)参考答案:
void fun(char *a,char *h,char *p)
{
int i=0;
char q=a;
while (q<h) /判断前导的结束/
{
a[i]=*q;q++;i++;
}
while (q<p) /*删除除了字符串前导和尾部的**/
1
{
if(q!='‘)
{
a[i]=*q;i++;
}
q++;
}
while (*q)
{
a[i]=*q;i++;q++;
}
a[i]=’\0’;
}
(77分40秒)总结:
此类题型在“删除指定字符”的基础上进行扩展,主函数已经将第一个字母与最后一个字母的指针传入,在做题时就可以切割成三大步:
(1)不停地给a[i]赋值,直到将下标移至第一个字母(本题通过a[i]=q来赋值,q<h的比较来控制移动)
(2)从第一个字母开始,直到最后一个字母,删除其中的号(本题通过a[i]=*q来赋值,q<p来控制移动)。删除方法同“删除指定字符”的程序。
(3)从最后一个字母开始,不停地给a[i]赋值,直到结束(本题通过a[i]=*q来赋值,q!=’\0’来控制移动。标准答案简写为q,请同学们用代入法自己推算)
(4)记住一定要给最后一个元素补上\0(这里为a[i]=’\0’)
六、从已排序的数组中删除重复元素
(79分53秒)编程题
请编写函数fun,该函数的功能是:删除一维数组中所有相同的数,使之只剩一个。数组中的数已按由小到大的顺序排列,函数返回删除后数组中数据的个数。
例如,若一维数组中的数据是:
2 2 2 3 4 4 5 6 6 6 6 7 7 8 9 9 10 10 10
删除后,数组中的内容应该是:
2 3 4 5 6 7 8 9 10。
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。
试题程序:
#include <stdio.h>
#define N 80
int fun(int a[], int n)
{
}
void main()
{
FILE wf;
int a[N]={ 2,2,2,3,4,4,5,6,6,6,6,7,7,8,9,9,10,10,10,10}, i, n=20;
printf(“The original data :\n”);
for(i=0; i<n; i++)
printf(“%3d”,a[i]);
n=fun(a,n);
printf(“\n\nThe data after deleted :\n”);
for(i=0; i<n; i++)
printf(“%3d”,a[i]);
printf(“\n\n”);
//
wf=fopen(“out.dat”,“w”);
for(i=0; i<n; i++)
fprintf(wf,“%3d”,a[i]);
fclose(wf);
//
}
参考答案:
int fun(int a[], int n)
{
int i,j=1;
for(i=1;i<n;i++)
if(a[j-1]!=a[i]) (87分46秒)讲解为什么从j-1开始
/若该数与前一个数不相同,则要保留/
{
a[j]=a[i];
j++;
}
return j;
/返回不同数的个数/
}
七、平移字符串
(88分58秒)编程题
请编写函数fun,该函数的功能是:移动字符串中的内容,移动的规则是把第1到第m个字符,平移到字符串的最后,把第m+1到最后的字符移到字符串的前部。
例如,字符串中原有的内容为ABCDEFGHIJK,m的值为3,移动后,字符串中的内容应该是DEFGHIJKABC。
注意:部分源程序给出如下。
请勿改动main函数和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。
试题程序:
#include <stdio.h>
#include <string.h>
#define N 80
void fun (char *w,int m)
{
}
void main()
{
FILE wf;
char a[N]= “ABCDEFGHIJK”,b[N]= “ABCDEFGHIJK”;
int m;
printf(“The origina string :\n”);
puts(a);
printf(“\n\nEnter m: “);
scanf(”%d”,&m);
fun(a,m);
printf(“\nThe string after moving :\n”);
puts(a);
printf(“\n\n”);
//
wf=fopen(“out.dat”,“w”);
fun(b,3);
fprintf(wf,“%s”,b);
fclose(wf);
//
}
参考答案:
(89分31秒)讲解理论
(90分47秒)讲解代码
void fun (char *w,int m)
{
int i,j;
char t;
for(i=1;i<=m;i++) /进行m次的循环左移/
{t=w[0];
for(j=1;w[j]!=‘\0’;j++) /从第2个字符开始以后的每个字符都依次前移一个字符/
w[j-1]=w[j];
w[j-1]=t; /将第1个字符放到最后一个字符中/
}
}
(95分59秒)总结:
字符串平移也就是数组平移,这种题干通常要
用2层循环,外层控制平移的次数,内层循环控制所有元素向前移动。可以说,有多少个元素,内层循环就要执行多少次。
因此,平移字符串的内层循环可以用’\0’来做终值,而其它类型的数组元素个数只能用函数形参传进来。