第二部分:类型转换
1、隐式类型转换(自动转换)
在有多种数据类型混合计算的时候,若未人为控制类型,系统会自动进行类型转换
转换的规则是:存储长度较短的转换成存储长度较长的,且不丢失数据
char--->short--->int--->unsigned int--->long--->unsigned long--->double
↑
float--------
练习:思考以下程序的输出结果
int main()
{
int a = 1;
unsigned int b = -6;
if(a+b>0)
{
printf("yes\n");
}
else
{
printf("no\n");
return 0;
}
答案:输出yes
注意:该练习仅适用于旧版编译器。由于新版本编译器的优化功能,程序仍然会输出no。在这里只需了解原理即可
2、赋值转换
如果赋值运算符两侧数据类型不一致,则在赋值时会发生赋值类型转换
1)int型与float型
将float类型赋值给int型时,会舍去小数部分。如:
int a = 3.5;
//此时a的值是3
将int类型赋值给float型时,数值会以float类型存储。如:
float f = 4;
//此时f的值是4.0
2)float型与double型
将double型赋值给float型时,截取7位有效数字存储在float类型中。但要注意不要超过float类型的存储范围(因为double类型存储范围比float类型大)
3)char型与int型
将int类型赋值给char类型时,直接截取低8位存储在char型中。
将char类型赋值给int类型时,分两种情况:
⑴无符号char类型(即unsigned char型),数据存储在int类型低8位,剩下24位补0
⑵有符号char类型,数据存储在int型低8位。若char首位是0,则int型剩下24位补0;若char首位是1,则int型剩下24位补1。
4)int型与long类型(只考虑int型与long类型长度不一致情况)
将long类型赋值给int类型时,直接截断数据,将低位原封不动存储在int型中。
将int类型赋值给long类型时,分两种情况:
⑴无符号int类型(即unsigned int型),数据存储在long类型低位,剩下位补0
⑵有符号int类型,数据存储在long类型低位。若int首位是0,则剩下位补0;若int首位是1,则剩下位补1。
3、强制类型转换
隐式类型转换或赋值转换有时不会得到我们预期的结果,这时我们可以使用强制类型转换来实现我们想要的类型转换结果。
强制类型转换:人为地将某类型转换为另一类型
强制类型转换符:()括号
用法:
(待转换的类型)变量
例如:
float a = 3.5;
(int)a;//将a强制转换成int类型
float pi = 3.1415926;
int x = (int)pi;
在这里x会变成3,而pi还是原数值(3.1415926)。即经过赋值运算后,pi的类型又变回浮点数,pi的值不会变。
练习:从键盘输入5个学生的成绩(整数),要求输出总成绩(整数)和平均成绩(小数)
答案:
int main()
{
int sum=0,input=0;
float avg;
scanf("%d",&input);
sum = sum + input;
scanf("%d",&input);
sum = sum + input;
scanf("%d",&input);
sum = sum + input;
scanf("%d",&input);
sum = sum + input;
scanf("%d",&input);
sum = sum + input;
printf("总成绩是%d\n",sum);
avg = (float)sum/5;
printf("平均成绩是%f\n",avg);
return 0;
}
/*************学过C语言的同学建议使用循环输入***************************/
int main()
{
int sum=0,input=0;
int i;
for(i=0;i<5;i++)
{
scanf("%d",&input);//每循环一次输入一个,循环五次
sum += input;
}
printf("%d\n",sum);
printf("%f\n",(float)sum/5);
return 0;
}
/***********对C语言精通的同学建议使用数组存储输入数据*******************/
int main()
{
int input[5]={0};
int i;
int sum = 0;
for(i=0;i<5;i++)
{
scanf("%d",input[i]);
sum += input[i];
}
printf("%d\n",sum);
printf("%f\n",(float)sum/5);
return 0;
}
第三部分:运算符与表达式
1、表达式
用运算符和括号将操作数连接起来的符合C语言语法规则的句子称为表达式。
如2*y+5中,*和+是运算符,2,y,5是操作数,整个句子符合C语言语法规则。
2、运算符
运算符用于执行程序代码运算,对一个以上的操作数进行运算。
在C语言中,运算符需要至少1个操作数(不超过3个)
在C语言中,把除了控制语句(如if,for等)和输入输出语句以外的几乎所有的基本操作都当做运算符处理。
3、运算符的分类
1)根据结合数的数目分类:
单目运算符:即操作数只有1个的运算符
双目运算符:即操作数有2个的运算符
三目运算符:即操作数有3个的运算符,只有一个( ?: )
2)根据运算符的用途分类:
赋值运算符:如=及其扩展运算符
逻辑运算符:如&&(与)、||(或)、!(非)
算数运算符:如+ - * / %等
关系运算符:如> < >= <= == !=等
位运算符:如<< >>等
条件运算符:只有一个 ?:
逗号运算符:只有一个 , 用于分隔
……
4、运算符的结合顺序
运算符有两种结合顺序:自左至右、自右至左
自左至右(左结合性):表示运算符的操作数是从左向右(先左再右)与运算符结合的,如加法+
例:3 + 5,即3加上5,先取3,再取+,再取5
自右至左(右结合性):表示运算符的操作数是从右向左(先右再左)与运算符结合的,如赋值=
例:a = 3,即先取数3,再给a赋值
5、运算符的优先级
C语言中运算符有优先级,从高到低分为15级(不同书籍划分方式不同,级数不同,这里采用百度百科划分方式),1级优先级最高,15级最低。
混合运算时,先算优先级高的,再算优先级低的(类比数学中,先算乘除,再算加减),同等级运算符则从左向右依次运算
6、算数运算符
符号及含义:+(加) -(减) *(乘) /(除) %(取余)
数目:双目运算符
结合性:自左至右
优先级:+和-:4级。*和/和%:3级
使用方法:表达式 运算符 表达式。如:3+5 a-b等
注意:
⒈C语言中,若相除2个数都是整数,则计算/时只取商,余数部分被舍去。
⒉%的操作数必须是整数
练习1:输入以下程序,查看输出结果
int main()
{
int a = 22;
int b = 5;
printf("%d\n",a/b);
return 0;
}
4
练习2:在练习1基础上,将变量类型改成float类型,查看输出结果
int main()
{
float a = 22;
float b = 5;
printf("%f\n",a/b);
return 0;
}
4.4
练习3:读以下程序,猜想输出的结果,再执行程序检验自己的猜想
#include <stdio.h>
int main()
{
double y;
float a = 2.0;
int b =6,c=3;
y = a*b/c-1.5+'A';
printf("%f\n",y);
return 0;
}
67.5
7、正运算符与负运算符
符号及含义:+(正数) -(负数)
数目:单目运算符
结合性:自右至左
优先级:2级
使用方法:+表达式 或 -表达式。如:
int a = 3;
printf("%d\n",-a);//对a取负数,会输出-3
8、关系运算符
符号及含义:>(大于) <(小于) >=(大于等于) <=(小于等于) ==(判等) !=(不等)
数目:双目运算符
结合性:自左至右
优先级:> < >= <=:6级。==和!=:7级
使用方法:表达式 运算符 表达式。如3<5,a==7等
注意:
⒈关系运算符只会返回0或1两个值。其中返回0代表假,返回1代表真。
练习1:输入以下程序,查看输出结果并思考为什么
int main()
{
int a = 3; (a>2 && 4>a)
if(4>a>2) 4>3是真,返回值为1, 而1>2为假 这种写法错误
{
printf("a is %d\n",a);
}
else
{
printf("error!\n");
}
return 0;
}
⒉在C语言中,所有非0的数代表真,0代表假。
练习2:输入以下程序,查看输出结果
int main()
{
int a = 100;
if(a)
{
printf("true\n");
}
else
{
printf("false\n");
}
a = 0;
if(a)
{
printf("true\n");
}
else
{
printf("false\n");
}
a = -100;
if(a)
{
printf("true\n");
}
else
{
printf("false\n");
}
return 0;
}
ture false ture
/*************练习题2,对于熟练使用C语言的同学建议使用?:表达式**************/
⒊注意判断两个表达式相等的运算符是==,与=(赋值运算符)要分开
9、自增运算符与自减运算符
符号及含义:++(自增) --(自减)
数目:单目运算符
结合性:自左至右 或 自右至左(取决于表达式位置)
优先级:2级
使用方法:
自增;表达式++ 或 ++表达式。如a++ 或 ++a
自减:表达式-- 或 --表达式。如b-- 或 --b
注意:
⒈自增自减运算符表示将表达式+1或-1。如:
int a = 3;
a++;
a会变成4。
⒉注意++/--运算符与+/-(正数/负数运算符)是同等优先级。当这两个运算符一起使用时,是右结合性。即-a++ <---等价于---> -(a++);
⒊++在前与++在后的区别(--同理):
++在后(如a++):先使用数,再加1
++在前(如++a):先加1,再使用数
若单独使用自增运算符时,a++ <---等价于---> ++a <---等价于---> a=a+1 <---等价于---> a += 1(见下)
练习1:思考以下问题,之后编写程序观察输出结果
若有int a = 3,b;
⒈b = a++ 与 b = ++a,b的值相同么?
⒉b = -a++的结果是什么?
答案:
⒈不同。第一个b=3,第二个b=4。
⒉b=-3。?????????
练习2:读以下程序,猜想输出的结果,再执行程序检验自己的猜想
#include <stdio.h>
int main()
{
int i = 8,j = 10,k = 12;
int m,n,p;
m = ++i;
n = j--;
p = (++m)*(n++)+(--k);
printf("i=%d,j=%d,k=%d\n",i,j,k);
printf("m=%d,n=%d,p=%d\n",m,n,p);
return 0;
}
答案:输出
i=9,j=9,k=11
m=10,n=11,p=111
10、逻辑运算符
符号及含义:&&(逻辑与) ||(逻辑或) !(逻辑非)
数目:!为单目运算符,&&和||为双目运算符
结合性:!为自右至左,&&和||为自左至右
优先级:!:2级。&&:11级。||:12级
使用方法:
⒈&&和||:表达式 运算符 表达式。如:a&&b a||b
⒉!:!表达式。如!a
注意:
逻辑运算符用于计算表达式的逻辑关系
⒈逻辑与:当且仅当两个表达式都为真时,则计算结果为真。否则为假
⒉逻辑或:当且仅当两个表达式都为假时,则计算结果为假。否则为真
⒊逻辑非:当原表达式为真时,计算结果为假;当原表达式为假时,计算结果为真
⒋逻辑运算符的返回结果同关系运算符,计算结果为真返回1,计算结果为假返回0
练习1:若要使得内容8的练习题1能够正确输出,则需要怎样改变程序?
答案:
int main()
{
int a = 3;
if(a>2 && a<4)
{
printf("a is %d\n",a);
}
else
{
printf("error\n");
}
return 0;
}
练习2:将内容8的练习题2的所有if(a)改写成if(!a),执行程序查看结果
/************************逻辑运算符的短路现象*****************************/
当多个逻辑运算符连续使用的时候,会出现逻辑运算符短路的情况
1、与(&&)运算符短路
表达式1 && 表达式2 && 表达式3
当表达式1是真时,才会去判断表达式2的真/假。否则,如果表达式1是假,则之后的都不会进行运算。
当表达式2是真时,才会去判断表达式3的真/假。否则,如果表达式2是假,则之后的都不会进行运算。
示例:
int a=0,b=1,c=2,d;
d = a++ && b-- && ++c;
程序的执行结果是
a->1,b->1,c->2,d->0
分析:
⒈首先会计算a++,计算a++会首先取出a的值,此时a的值是0
⒉因为此时a的值是0,表达式1(a++)的位置是假,发生短路,后面的表达式2(b--)和表达式3(++c)都不会进行运算,此时返回假(0),赋值给d
⒊执行a++(a的值加1)
2、或(||)运算符短路
表达式1 || 表达式2 || 表达式3
当表达式1是真时,跳过判断表达式2和表达式3,直接返回真;当表达式1是假时,才去判断表达式2的真/假
当表达式2是真时,跳过判断表达式3,直接返回真;当表达式2是假时,才去判断表达式3的真/假
示例:
int a=0,b=1,c=2,d;
d = a++ || b-- || ++c;
程序的执行结果是
a->1 b->0 c->2 d->1
分析:
⒈首先会计算a++,计算a++会首先取出a的值,此时a的值是0
⒉因为此时a的值是0,表达式1(a++)的位置是假,需要判断表达式2(b--)的值
⒊计算b--,首先取出b的值,此时b的值是1
⒋因为此时b的值是1,表达式2(b--)的位置是真,发生短路,无需判断表达式3(++c)的真/假,此时返回真(1),赋值给d
⒌执行a++(a的值加1)和b--(b的值减1)
思考1:
如若将以上2个示例改写成:
d = a++ || b-- && ++c;
程序的执行结果是什么?先猜想结果,再上机验证
答案:
a->1 b->0 c->3 d->1
思考2:
如若将以上2个示例改写成:
int a=1,b=2,c=3,d;
d = a++ || b-- && ++c;
程序的执行结果是什么?先猜想结果,再上机验证
答案:
a->2 b->2 c->3 d->1
注意:这里要特别注意&&的运算优先级比||高。运算优先级高并不代表会先运算,而是代表先与操作数结合。而整个算式还是按照自左至右顺序计算。
在示例2中,因为&&的优先级比||高,因此算式等价于:
d = a++ || (b-- && ++c);
此时进行自左至右的运算,当遇到a为真(1)的时候,整个算式发生了短路,括号内的b--和++c都不会执行
在这里要特别注意运算优先级高不代表会先运算,而是代表先与操作数结合
/***********************逻辑运算符的短路现象end***************************/
练习3:设int a=3,b=4,c=5,d;
分别求出下列表达式的d的值(即判断此表达式真假)
1)d = a+b>c && b==c
2)d = a || b+c && b-c
3)d = !(a>b) && !c || 1
4)d = !(x=a)&&(y=b)&&0(x和y均为int型,求出x和y的值)
5)d = !(a+b)+c-1 && b+c/2
答案:
1)d=0
2)d=1
3)d=1
4)d=0,x=3,y未知 a赋值给x,这个语句肯定为真,得1;!1得0,为假,直接短路
5)d=1 a+b--->7 !(a+b)得0 0+c-1--->2 b+c/2---->6.5 都为真,得1
11、赋值运算符及复合赋值运算符
符号及含义:=(赋值) +=(加后赋值) -=(减后赋值) *=(乘后赋值) /=(除后赋值) %=(取余后赋值) <<=(左移后赋值) >>=(右移后赋值) &=(按位与后赋值) ^=(按位异或后赋值) |=(按位或后赋值)
数目:未知(取决于是否存在多重赋值)
结合性:自右至左
优先级:14级
使用方法:
一次赋值:待赋值 运算符 表达式。如a=3,a=5+4,a+=5等
多重赋值:将同一个值赋给多个变量的操作,如a=b=c=10。此时a,b,c都为10
复合赋值:先将左值取出,之后与右值进行运算(运算取决于运算符),将运算后的结果再赋值给左值。如:a += 3 -----> a = a + 3
练习:思考以下问题,之后编写程序观察输出结果
⒈int a = 2,b = 3,c = 4;
a *= b+c;
问:a的值是多少?14 优先级
⒉int a = 3;
a += a *= a; 从右到左计算,结合性
问:最后a的值是多少?
答案:⒈a的值是14。a *= b+c -----> a = a*(b+c);
⒉a的值是18。a += a *= a ---> 先算a = a*a(此时a是9),再算a = a+a(此时a是18)
12、条件运算符
符号及含义:?:(判断?前表达式是否成立,若成立取:前表达式,否则取:后表达式)
数目:三目运算符(C语言中唯一的三目运算符)
结合性:自右至左
优先级:13级
使用方法:表达式1?表达式2:表达式3。先判断表达式1是否成立,若成立取表达式2,否则取表达式3。
条件运算符相当于小型的if-else表达式
练习1:读以下程序,先说明程序的功能,再输入程序并执行验证自己的猜想
int main()
{
int max,a,b;
scanf("%d %d",&a,&b);
max = a>b?a:b;
printf("%d\n",max);
return 0;
}
练习2:读以下程序,猜想输出的结果,再执行程序检验自己的猜想
int main()
{
int a = 2,b = 3;
printf("%d\n",--a==b++?a++:b++);
//思考:当程序执行到这里,a的值和b的值都是多少?
return 0;
} a=1 b=5
13、长度运算符
符号及含义:sizeof()(计算括号内数据类型或表达式的大小)
数目:无
结合性:无
优先级:2级
使用方法:sizeof(表达式) 或 sizeof(类型名)
sizeof()用于计算某类型或某表达式所占内存的大小,经常与结构体/数组等连同使用
14、位运算符
注意:位运算符的操作数只能是整数或字符型数。当操作数进行位运算的时候,操作数转换为2进制数。
1)按位运算符
符号及含义:&(按位与) ^(按位异或) |(按位或) ~(按位取反)
数目:&和^和|:双目运算符。~:单目运算符。
结合性:&和^和|:自左至右。~:自右至左
优先级:
~:2级
&:8级
^:9级
|:10级
使用方法:按位运算会先将操作数转换成2进制数,对每一个位(bit)进行运算
按位与&:1&1=1 1&0=0 0&1=0 0&0=0(当且仅当全是1时值才为1,否则为0)
按位异或^:1^1=0 1^0=1 0^1=1 0^0=0(当且仅当两个数不相同时值才为1,否则为0)
按位或|:1|1=1 1|0=1 0|1=1 0|0=0(当且仅当全是0时值才为0,否则为1)
按位取反~:1-->0 0-->1(若是1则变成0,若是0则变成1)
注意:要将&(按位与)和&&(逻辑与)、|(按位或)和||(逻辑或)区分开
练习1:读以下程序,思考程序逻辑,猜想程序的功能,之后上机检验
#include <stdio.h>
int main()
{
int a = 2,b = 3;
a ^= b;
b ^= a;
a ^= b;
printf("%d\n%d\n",a,b);
return 0;
}
答案:三步异或法可以实现交换2个整数
(交换2个整数的3种方法:申请中间变量法;三步异或法;加和分减法)
练习2:思考如何使用按位操作使一个数清零
答案:将这个数&0即可 或 将这个数^自身
练习3:在不使用%2的情况下,如何判断一个整数是奇数还是偶数?
答案:将这个整数与1做按位与计算,若结果是1则是奇数,若结果是0则是偶数
练习4:思考(6&6)>4 和 6&6>4的结果一样吗?为什么?
答案:(6&6)>4的结果是true,6&6>4的结果是0。因为>的优先级高于&,若不加括号则会先计算6>4(结果是1),然后计算6&1(结果是0)
练习5:如何将一个数的所有位进行翻转?如0x0f--->0xf0(1变0,0变1)
答案:将这个数与所有位全是1的数做异或运算
0x0f ^ 0xff ---> 0xf0
在进行位运算时一定要注意运算符优先级。对于不确定优先级的,我们要学会使用()来分隔运算符和操作数
2)移位运算符
符号及含义:<<(左移) >>(右移)
数目:双目运算符
结合性:自左至右
优先级:5级
使用方法:表达式1 左移/右移 表达式2。如a<<3
左移/右移运算符是先将操作数转换成2进制数,然后将这个数向左/右位移x(表达式2的值)位
左移的高位会溢出,直接舍去。低位会补0
右移的低位会溢出,直接舍去。高位补0还是补1见以下规则:
⒈对于无符号数,高位补0
⒉对于有符号数,若为非负数(即最高位(符号位)是0),则高位也是补0。若为负数,在高位补0还是补1取决于系统。补0的称之为“逻辑右移”,补1的称之为“算数右移”(大多数是后者)
对于整数,左移x位相当于乘2的x次方,右移x位相当于除2的x次方
练习1:输入以下程序,查看输出结果
int main()
{
int a = 64;
a = a<<4;
printf("%d\n",a);
a = 64;
a = a>>4;
printf("%d\n",a);
return 0;
}
1024 4
练习2:使用位运算符,输入整数k和num,计算以下内容:
⒈取出num的第k位的数
⒉将num的第k位置0
1010 0111 0100 右移6位 得到1010 0
(num>>k)&1
~(1<<k)&num
位运算符的操作是非常快的(几乎可以忽略它的耗时),因此从程序优化角度来考虑,使用位运算符来代替一些耗时操作(如/,%等)是十分必要的。
练习3:在不使用%的情况下,怎样计算num%8的值?(num为整数)
答案:num &= 7即可
num&((2^k)-1) 8是2^3
15、逗号运算符
符号及含义:,(分隔作用)
数目:未知
结合性:自左至右
优先级:15级(最低)
使用方法:表达式,表达式……
逗号运算符是运算符里优先级最低的运算符,仅仅起到分隔的作用。
第二部分:选择结构1、C语言程序有3种基本结构:顺序结构、选择结构和循环结构1)顺序结构:所谓顺序结构,就是从上到下的所有语句都会依次执行2)选择结构:选择结构中存在一条(或多条)分支语句,当满足分支的条件时语句才会执行,否则不会执行3)循环结构:循环结构即是在某些条件的控制下重复执行一段代码语句,当满足循环条件时执行循环语句,否则不执行循环语句。4)流程图:开始/结束使用圆角矩形,一般语句使用矩形,选择语句使用菱形,各形状使用有向箭头连接起来。2、选择结构if-elseif(表达式)-else是最常见的2分支判断选择结构,常见的if()-else有3种形式:⒈if(表达式){语句块……}⒉if(表达式){语句块1……}else{语句块2……}⒊if(表达式1){语句块1……}else if(表达式2){语句块2……}....else {语句块n}……1)if(表达式)用法:if(表达式){语句块;……}说明:当程序执行到if语句时,会判断if()内表达式的真假,若为真,则会执行语句块,否则跳过语句块示例:int main(){int a = 5;if(a>0){printf("a大于0\n");//满足if条件,这条语句会执行}a = -5;if(a>0){printf("a大于0\n");//不满足if条件,这条语句不会执行}return 0;}执行结果:输出一条"a大于0"2)if(表达式)-else用法:if(表达式){语句块1;……}else{语句块2;……}说明:当程序执行到if语句时,会判断if()内表达式的真假,若为真,则会执行if下语句块1;若为假,则会执行else下语句块2示例:int main(){int a = 5;if(a>0){printf("a大于0\n");}else{printf("a小于0\n");}a = -5;if(a>0){printf("a大于0\n");}else{printf("a小于0\n");}return 0;}3)多个if(表达式)-else用法:if(表达式1){语句块1……}else if(表达式2){语句块2……}……//可使用多个else//最后一个以else结尾{语句块3……}说明:当程序执行到if(表达式1)时,会判断表达式1的真/假。当表达式1为真时,执行语句块1;否则,执行if(表达式2),会判断表达式2的真/假依次进行每个if(表达式)的判断如果以上if(表达式)都为假,则执行最后的else语句后的语句块3实际上多个if()-else表达式就是使用多个2分分支判断实现了多重分支判断示例:程序输入成绩,为0~100之间的整数。之后输出成绩代表的分数段:90~100为优,80~89为良,70~79为中,60~69为及格,0~59为不及格,其他则输出错误信息#include<stdio.h>#include<stdlib.h>int main(){int score;printf("请输入学生成绩:");scanf("%d",&score);if(score<=100 && score>=90){printf("成绩为优\n");}else if(score<90 && score>=80){printf("成绩为良\n");}else if(score<80 && score>=70){printf("成绩为中\n");}else if(score<70 && score>=60){printf("成绩为及格\n");}else if(score<60 && score>=0){printf("成绩为不及格\n");}else{printf("输入非法数据\n");}return 0;}4、如果if()或者else表达式下未使用{}限制,则只会执行if()或else下的第一条语句。示例:#include<stdio.h>int main(){int a = 5;if(a>0)printf("a是正数\n");elsea = -3;printf("a不是正数\n");printf("a is %d\n",a);return 0;}程序输出结果:a是正数a不是正数a is 5尽管在本例中,else下的两句表达式缩进相同(以代表两句都隶属于else语句块)但是由于else后没有{},因此实际上隶属于else的语句只有第一句(即a=-3,如果能执行的话),而printf()语句则会照常执行。因此我们建议使用{}将if()和else后的语句都括起来,以免出现逻辑错误上面输入成绩输出分数段的示例等价于:#include<stdio.h>int main(){int score;printf("请输入学生成绩:");scanf("%d",&score);if(score<=100 && score>=90)printf("成绩为优\n");else if(score<90 && score>=80)printf("成绩为良\n");else if(score<80 && score>=70)printf("成绩为中\n");else if(score<70 && score>=60)printf("成绩为及格\n");else if(score<60 && score>=0)printf("成绩为不及格\n");elseprintf("输入非法数据\n");return 0;}练习1:输入3个整数,从小到大输出答案:int main(){int a,b,c,tmp;//tmp用于交换值的中间变量scanf("%d %d %d",&a,&b,&c);if(a>b && a>c)//此时a是最大值{tmp = a;a = c;c = tmp;}else if(b>c && b>a)//此时b是最大值{tmp = b;b = c;c = tmp;}//程序执行到这,c中存的是3个数中最大值,剩下a,b两个数待比较if(a>b){tmp = a;a = b;b = tmp;}printf("%d %d %d\n",a,b,c);return 0;}练习2:输入3个正整数作为3条线段的长度,判断这3条线段能否构成三角形。构成三角形的条件是:任意两边之和大于第三边答案:#include<stdio.h>int main(){ int bian1,bian2,bian3; int flag=0;//用于标识状态//flag状态:0能构成三角形、1输入非法数据、2不能构成三角形 printf("请输入三角形的3条边:\n"); scanf("%d%d%d",&bian1,&bian2,&bian3); if(bian1<=0 || bian2<=0 || bian3<=0) flag=1; else if(bian1+bian2<=bian3 || bian1+bian3<=bian2 || bian2+bian3<=bian1) flag=2;//程序执行到这里,flag获得状态 if(flag==0) printf("三条边能构成三角形\n"); else if(flag==1) printf("输入数据错误\n"); else if(flag==2) printf("三条边不能构成三角形\n"); return 0;}5、if()的嵌套使用在if()-else语句中又包含一个或多个if()-else语句称为if()的嵌套使用。一般形式如下:if(表达式1){if(表达式2){语句块1……}else{语句块2……}}else{if(表达式3){语句块3……}else{语句块4……}}程序的执行逻辑:表达式1为真:表达式2为真:执行语句块1否则:执行语句块2否则:表达式3为真:执行语句块3否则执行语句块4示例:输入一个整数,判断这个整数是正整数、0还是负整数1)使用多个if()-elseint main(){int input;scanf("%d",&input);if(input>0)printf("输入的是正整数\n");else if(input==0)printf("输入的是0\n");elseprintf("输入的是负整数\n");return 0;}2)使用if()嵌套int main(){int input;scanf("%d",input);if(input>0)printf("输入的是正整数\n");else{if(input==0)printf("输入的是0\n");elseprintf("输入的是负整数\n");}return 0;}练习:输入一个年份(正整数),判断这年是否是闰年闰年判断标准:年份能被4整除;如若遇到100的倍数,则需判断年份能否被400整除。(逢4一闰,逢百不闰,逢400又闰)如1900年不是闰年,1904年是闰年,2000年是闰年答案:int main(){int input;scanf("%d",&input);if(input%4==0){if(input%100!=0){printf("%d是闰年\n",input);}else if(input%400==0){printf("%d是闰年\n",input);}else{printf("%d不是闰年\n",input);}}else{printf("%d不是闰年\n",input);}return 0;}6、if()和else的配对(当没有{}限定的时候)当else与if配对的时候,else总是与它上面最近的未配对的if()结合if()if()语句1else 语句2if()语句3else 语句4在这段程序中,虽然编程人员将else 语句2放在与第一个if()同一缩进上以表示该else与首个if()结合,但实际上这个else是与它的上一个if(即if()语句1)结合。因此在使用if()-else语句(尤其在嵌套使用时)一定要使用{}来限定
7、switch()语句if()-else语句只能判断2个分支,若要判断多个分支则需要if()-else的多次使用或嵌套使用,十分不便switch()语句是多分支选择语句。通过switch()的多分支判断可以简便地实现多分支选择结构switch()语句的一般形式如下:switch(表达式){case 常量1:语句1;break;case 常量2:语句2;break;……//多个case结构case 常量n:语句n;break;default: default 缺省语句n+1;}说明:1)switch(表达式)表达式的值应是一个整数(包括字符数据) 必须是个整数或者字符数2)switch()下的{}是一段语句块,这段语句包含若干个以case开头的语句块和至多一个以default开头的语句块3)case后需要一个常量(或常量表达式),case和default都是标号作用。首先判断switch(表达式)的表达式的值,之后与各个case之后的值进行比对,如果某个case后的值与表达式的值相同,则跳转到此case语句;如果所有的case都不匹配,则跳转到default后的语句4)可以没有default语句。若没有default语句,则如果没有匹配的case,则程序不执行任何语句5)各个case之间的先后顺序以及default的位置不影响程序执行结果,不过我们强烈推荐按一定顺序排列case语句,default语句放在最后6)每个case语句后的常量值必须各不相同,否则会发生互相矛盾现象7)break语句用来终止switch()语句。如果case语句后没有break语句,则当匹配到合适的case后,程序会一直执行接下来的语句直至遇到break或switch()结束示例:使用switch()实现:输入一个正整数,输出对应的星期。如输入1代表星期一,输入2代表星期2……输入7代表星期日。输入其他数输出错误信息#include<stdio.h>int main(){int input;scanf("%d",&input);switch(input){case 1:printf("今天是星期一\n");break;case 2:printf("今天是星期二\n");break;case 3:printf("今天是星期三\n");break;case 4:printf("今天是星期四\n");break;case 5:printf("今天是星期五\n");break;case 6:printf("今天是星期六\n");break;case 7:printf("今天是星期日\n");break;default:printf("输入错误\n");}return 0;}在示例中,如果去掉所有的break语句,则:程序执行结果:输入4,输出:今天是星期四 今天是星期五 今天是星期六 今天是星期日 输入错误练习1:将上文的“输入学生成绩,输出优、良、中、及格、不及格”问题使用switch()语句来实现答案:int main(){int input;printf("请输入学生成绩:"); 大于100的情况没写,在之前加个if elsescanf("%d",&input);input /= 10;switch(input){case 10:case 9:printf("成绩为优\n");break;case 8:printf("成绩为良\n");break;case 7:printf("成绩为中\n");break;case 6:printf("成绩为及格\n");break;case 5:case 4:case 3:case 2:case 1:case 0:printf("成绩为不及格\n");break;default:printf("输入非法数据\n");}return 0;}注意在case 10处,以及case 5~case 0处均没有break,则匹配到这些case后会顺序执行之后的语句直至出现break或switch()语句结束练习2:运输公司对用户按路程计算费用。路程越远,每吨*千米运费折扣越高。计算运费公式如下:freight = weight * distance * price * (1-discount);路程与折扣的关系如下:s<250:无折扣250<=s<500:2%折扣500<=s<1000:5%折扣1000<=s<2000:8%折扣2000<=s<3000:10%折扣3000<=s:15%折扣要求从键盘输入货运总重(单位吨)、货运路程(单位千米)、每吨*千米货运单价(单位元),输出总花费答案:#include<stdio.h>int main(){int distance,c;float price,weight,discount,freight;printf("请输入货运总重量:");scanf("%f",&weight);printf("请输入货运路程:");scanf("%d",&distance);printf("请输入单价:");scanf("%f",&price);if(distance>=3000)c=12;elsec=distance/250;switch (c){case 0:discount=0;break;case 1:discount=2;break;case 2:case 3:discount=5;break;case 4:case 5:case 6:case 7:discount=8;break;case 8:case 9:case 10:case 11:discount=10;break;case 12:discount=15;break;}freight = price * distance *weight * (1-discount/100);printf("总运费是%f\n",freight);return 0;}
第一部分:复习1、C语言程序有3中基本结构:顺序结构、选择结构、循环结构2、if()-else语句:判断if()内表达式是真/假,若为真则执行if()下的语句,否则执行else下的语句3、if()-else语句可以多次使用4、if()-else语句可以嵌套使用5、如果if()-else语句下没有{}限制,则只执行if()和else下的第一条语句6、else与if配对遵循就近原则,即:优先与上一个最近的未配对的if()结合7、switch()语句用于多重分支选择。switch()的值与每个case相匹配,若与case后的值相同则执行case后的语句直至遇到break语句或switch()语句结束;否则则执行default后的语句8、break在switch()语句中的作用是执行后跳出switch()语句
第二部分:循环语句在生活中我们常常遇到需要重复处理的问题,我们在编程时解决需要重复处理的问题需要使用循环语句循环语句主要有3种:while()循环;do-while()循环和for()循环1、while()循环用法:while(循环条件){循环体;……}说明:当程序遇到while()循环的时候,首先会判断while()的括号内的表达式,若为真(即满足循环条件)则执行循环,执行完循环体后再次返回到while();若为假则结束循环示例:输入10个学生的成绩,输出这10个学生的成绩总和以及平均分int main(){int i = 1,count = 0,input = 0;//i用来控制循环次数,count用于总和,input用于每次输入float avg = 0;//avg用于平均分while(i<=10)//循环条件{printf("请输入第%d个学生的成绩:\n",i);scanf("%d",&input);count += input;i++;//不要忘了让i++}avg = (float)count/10;printf("总成绩是%d\n平均成绩是%f\n",count,avg);return 0;}思考:若在循环体中没有i++这一句将会发生什么结果?答案:i++这一句的作用是改变循环的状态。若没有i++这句,则i的值会永远是1,则循环永远都不会结束,即程序会进入死循环初学者在使用循环时一定要注意不要陷入死循环。当你的程序使用了循环结构,在编译阶段无语法错误,在执行时发现程序会无限期进行下去,则很可能你的程序进入了死循环。此时要检查循环条件设置是否正确,以及检查是否设置了改变循环状态的量。若发生了死循环,按ctrl+c组合键可强制终止当前程序练习:求1+2+3+……+100=?答案:int main(){int i=0,sum=0;while(i<=100){sum += i;i++;}printf("和是%d\n",sum);return 0;}
若while()后没有{}限制,则循环只执行到while()后第一个分号处(即只执行一条语句),这点与if()-else相同
2、do-while()循环用法:do{循环体;……}while(循环条件);说明:当程序遇到do,会首先执行do下面的语句(即循环体),之后执行到while(),判断while()的括号内的表达式是真/假,若为真(即满足循环条件)则返回do语句处再次执行循环体;若为假则结束循环。示例:将上文示例改写成do-while()循环int main(){int i = 1,count = 0,input = 0;//i用来控制循环次数,count用于总和,input用于每次输入float avg = 0;//avg用于平均分do//循环开始{printf("请输入第%d个学生的成绩:\n",i);scanf("%d",&input);count += input;i++;//不要忘了让i++}while(i<=10);//循环条件avg = (float)count/10;printf("总成绩是%d\n平均成绩是%f\n",count,avg);return 0;}练习:将上文练习“1+2+3+……+100=?”改写成do-while()循环答案:int main(){int i=0,sum=0;do{sum += i; i++; sum+=i++}while(i<=100);printf("和是%d\n",sum);return 0;}
while()与do-while()的区别:while()循环是先判断循环条件,再进入循环体。而do-while()循环是先进入循环体,再判断循环条件。while()循环有可能不执行循环体,而do-while()循环一定会执行一次循环体
3、for()循环用法:for(表达式1;表达式2;表达式3){循环体;……}注意:for()括号内的3个表达式的分隔符是分号;不是逗号,说明:表达式1:循环的初始条件,只执行一次。可以为0个、1个或多个变量设置初值表达式2:判断循环结束的条件。在每次执行循环体前判断此表达式,若表达式为真则进入循环,否则不执行循环表达式3:作为循环的调整(即改变循环状态),在执行完循环体之后执行for(表达式1;表达式2;表达式3){循环体;}等价于:
表达式1;while(表达式2){循环体;表达式3;}表达式1可以省略。若如此做,相当于没有给for()循环设定起始初值。因此,若要循环正常进行,需要在for()之前设置好循环起始初值表达式2可以省略。若如此做,相当于for()循环没有结束条件,即死循环。等价于:while(1){循环体;表达式3;}表达式3可以省略。若如此做,需要在循环体内设置循环调整语句,否则循环无法正常执行。若将3个表达式都省略(即for(;;)),则相当于设置了一个无限循环若for()后没有{}限制,则循环只执行到for()后第一个分号处(即只执行一条语句),这点与if()-else相同示例:将上文示例改写成for()答案:int main(){int i,count=0,input=0;float avg;for(i=0;i<10;i++){printf("请输入第%d个学生的成绩:\n",i);scanf("%d",&input);count += input;}avg = (float)count/10;printf("总成绩是%d\n平均成绩是%f\n",count,avg);return 0;}练习:将上文练习“1+2+……+100”改写成for()循环答案:int main(){int i,sum=0;for(i=1;i<=100;i++)sum += i;printf("和是%d\n",sum);}三种循环的比较:1)如果使用while()或do-while(),需要注意在循环体内设置改变循环状态的变量。而for()循环则是在表达式3中设置2)一般来说,for()循环的表达式3的位置甚至可以将循环体都放入,所以for()循环更常用3)在知道循环次数的情况下,推荐使用for()循环;在不知循环次数的情况下推荐使用while()或do-while()循环4)从C99版本开始,C语言支持以下用法:for(int i=0;i<10;i++){循环体;……}这样就可以不用事先特地定义一个用于循环的变量,随时使用随时定义即可。注意1:这种使用方式循环变量i的生命周期仅仅是在for()循环中,当for()循环结束i即结束其生命周期。如果这样使用:for(int i=0;i<10;i++){循环体;……}printf("%d",i);//此时i的生命周期已经结束,企图访问不存在的变量i,非法则编译会显示语法错误。注意2:这种用法仅支持C99及以后C语言版本的编译器,如果这样使用出现语法错误则证明其编译器版本不支持这种语法,请不要使用这种语法而是在for()循环开始前就定义循环变量。
练习:打印斐波那契数列的前n项,其中n由键盘输入。斐波那契数列的特点如下:F1=1 (n=1)F2=1 (n=2)Fn=Fn-1 + Fn-2 (n>=3)答案:int main(){int f1=1,f2=1,i,n;printf("请输入循环次数:");scanf("%d",&n);for(i=1;i<=n/2;i++)//因为每次打印2个数,所以循环次数是n/2次{printf("%d\n%d\n",f1,f2);f1=f1+f2;f2=f2+f1;}return 0;}
4、循环的嵌套使用一个循环体内又包含一个循环体的结构称之为循环的嵌套。内嵌循环还可以再嵌套循环。3种循环都是可以互相嵌套使用的。例如下面的例子都是合法的:1)while(){do{}while();}2)for(;;){while(){}}3)do{for(){}}while();练习1:输出如下图形: * 外层 控制行数 当前第I行 *** 内层 当前字符 ***** ****************……其中需要输出几行由键盘输入。答案:#include<stdio.h>int main(){int len,i,j;printf("please input line:");scanf("%d",&len);for(i=0;i<len;i++){for(j=0;j<len-i-1;j++) 此次循环结束打印出改行所有空格,再执行下面的for循环printf(" ");for(j=0;j<2*i+1;j++) 该循环打印出该行*号printf("*");printf("\n"); } return 0;}1.外循环,第一次是第一行,该行标记为i=0(实际的第一行)2.进入内循环: 在i=0的这一行打印空格和*号, 先在该行打印len-i-1个空格,打印结束后,进入下面的循环,在该打印*号,结束号,结束内循环;接着打印“\n”(内循环之外,外循环之内)3.再次进入外循环,此时i=1,即换到第i=1(实际的第二行)行,继续下面两个内循环。
练习2:现有100元钱要买以下3种文具。其中一个笔记本20元,一套尺6元,一个笔4元,每样文具至少买1个。列举出所有的购买方案,以及列举出所有花光100元的购买方案答案:int main(){int i,j,k,count=0;//计算总共多少方案for(i=1;i<=5;i++){for(j=1;j<=16;j++){for(k=1;k<=25;k++){if(i*20+j*6+k*4<=100){printf("买%d个笔记本,%d套尺,%d个笔,共%d元\n",i,j,k,i*20+j*6+k*4);count++;}}}}printf("共%d种\n",count);count=0;//计算花光100元总共多少种方案printf("花光100元的方案有:\n");for(i=1;i<=5;i++){for(j=1;j<=16;j++){for(k=1;k<=25;k++){if(i*20+j*6+k*4==100){printf("买%d个笔记本,%d套尺,%d个笔\n",i,j,k);count++;}}}}printf("花光100元共%d种\n",count);return 0;}5、提前结束循环的语句:continue、break、return、exit()1)continuecontinue语句的用法是:continue;其作用是结束本次循环,即跳过所有continue下的语句,进入下次循环。示例:输出100~200之间不能被3整除的整数int main(){int n;for(n=100;n<=200;n++){if(n%3==0)continue;//结束本次循环,进入下次循环 执行该语句后后面的语句都不执行,继续返回,进入下次循环printf("%d ",n);}printf("\n");return 0;}2)breakbreak语句的用法是:break;其作用是结束循环,执行循环体外的下一个语句回顾:break在switch()语句中的作用break在switch()语句中的作用是跳出switch()语句,执行switch()语句下一条语句。示例:从r=1开始,输出所有半径是正整数的圆的面积,直至出现面积大于100为止#include<stdio.h>#define PI 3.141592int main(){int r=1;float area;while(1)//死循环{area = r*r*PI;printf("半径%d的圆面积是%f\n",r,area);r++;if(area>=100)break;}return 0;}可以看到,尽管在示例程序中while()循环使用了死循环,当出现break语句后,依然会跳出循环。3)returnreturn语句的用法是:return 需要的返回值;其中需要的返回值由函数类型决定。如main()函数是int类型,则需要返回一个整数。如果函数是void类型则无需写返回值。return语句的作用是结束当前函数,并将返回值返回给函数调用的位置。有关return语句的用法我们将在函数的课程中详细讲解。需要注意的是return不仅仅是结束了循环,更是结束了当前的函数。因此要慎用。4)exitexit语句的用法是:exit(x);//x为0或正整数当x为0时表示正常结束,不为0时表示异常结束(异常号)exit()在头文件 stdlib.h 中exit()不仅仅是结束循环,它的真正意义是退出当前程序。因此非常不推荐使用exit()结束循环从效果来看,4个语句的效果如下:exit() > return > break > continue强<---------------------------->弱
练习:输出100~200间的所有素数答案:int main(){int i,j;for(i=100;i<=200;i++)//外层循环,表示当前数字{for(j=2;j<=i/2;j++)//内层循环,表示除数{if(i%j==0){break;}}if(j>=i/2){printf("%d是质数\n",i);}}return 0;}
6、goto语句用法:goto 语句标号;说明:goto语句是无条件跳转语句,当执行到goto语句时,程序会跳转到goto语句所指向的标号处。语句标号的命名规则与C语言标识符的命名规则相同。示例:int main(){label_1:…………goto label_1;}滥用goto语句会使程序无规律、可读性差。goto语句违背了C语言的模块化编程的基本思想,因此goto语句不推荐使用。