当前位置 博文首页 > jtwqwq的博客:翁恺老师C语言程序设计网课(6)
bool类型
首先要包含头文件#include<stdbool.h>,然后可以使用bool,true,false。
bool b=6>5;
只要bool量不是0,都=1.
逻辑运算是对逻辑量进行的运算,结果只有0或1。
逻辑量是关系运算或逻辑运算的结果。
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
! | 逻辑非 | !a | a的true或false反转 |
&& | 逻辑与 | a&&b | 只有a&b都是true时结果才是true |
|| | 逻辑或 | a||b | 只有a&b都是false时结果才是false |
例:表达数学区间时,(4,6)或[4,6]:
同样像之前if一样,不可以写4<x<6这种式子。因为4<x的结果是一个逻辑值(0或1)
x>4&&x<6
或x>=4&&x<=6
判断一个字符是大写字母:c>='A'&&c<='Z'
优先级:!>&&>||
例:!done&&(count>MAX)
:done是0且count>MAX
时结果为1
所有的优先级
优先级 | 运算符 | 结合性 |
---|---|---|
1 | () | 从左到右 |
2 | !,+,-,++,– | 从右到左(单目的+和-) |
3 | *,/,% | 从左到右 |
4 | +,- | 从左到右 |
5 | <,<=,>,>= | 从左到右 |
6 | ==,!= | 从左到右 |
7 | && | 从左到右 |
8 | || | 从左到右 |
9 | =,+=,-=,*=,/=,%= | 从右到左 |
短路:逻辑运算自左向右,如果左边足以决定结果,就不会进行右边的计算了
a==6&&b==1
如果左边a!=6
就终止了。
即:&&左边false就不做右边了;||左边是true就不做右边了。就算右边是赋值的话右边也不会做了。
int a=-1;
if(a>0&&a++>1){
printf("OK\n");
}printf("%d\n",a);
这样输出结果a=-1,没有进行右边的a++。
所以不要把赋值和复合赋值组合进表达式!
条件运算符1:问号
cnt=(cnt>20)?cnt-10:cnt+10;
//格式:条件?条件满足时的值:条件不满足时的值
相当于
if(cnt>20)cnt-=10;
else cnt+=10;
这种条件运算符?
的优先级高于赋值运算符,但是低于其他运算符,但是这样会很麻烦。
条件运算符自右向左,先计算右边的分支
不建议使用嵌套的条件表达式!太复杂,太难理解
条件运算符2:逗号
逗号是个运算符,连接两个表达式,并且以右边的表达式的值作为其结果。
逗号的优先级是最低的,所以两边的表达式会先运算,而右边的表达式的值留下来作为运算的结果。
i=3+4,5+6;//编译时会提示warning。因为赋值的优先级也要高于逗号,该式子其实是
(i=3+4),(5+6);//右边编译错误,i=7
如果写作i=(3+4,5+6);//编译时同样会warning,因为3+4没有用到;i=11
逗号表达式主要在for中使用
for(i=0,j=10;i<j; i++,j--)
目前来说,逗号表达式只有这一个用处。
回想起区间[m,n]内素数求和的例子,对于每个数我们定义isPrime=1
,如果最后仍然=1的话sum+=i
.
这样让这一层循环显得很大,而这一层代码的功能很单纯
我们可以以函数的形式把这一堆代码取出来
int isPrime(int i)
{
int ret=1;
int k;
for(k=2;k<i-1;k++){
if(i%k==0){
ret=0;
break;//有1~i-1间的因数就可以判断i不是素数了,break节约时间
}
}
return ret;
}
我们之前从第一个程序printf
开始就是在用函数。只不过这次我们在自己定义了一个函数。
这样main
里面很简洁,而且isPrime
可以很方便地多次使用
PS: 程序中出现几段几乎完全相似的代码(除了字母不同),“代码复制”是程序不良的表现。因为将来做修改、维护的时候要维护很多处。
(吓得我立刻就去改大作业了)我们用函数替换,可以把重复的代码提出。
//闭区间内所有数求和的函数
void sum(int begin,int end)
{
int i,sum=0;
for(i=begin;i<=end;i++){
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
int main()
{
sum(1,10);
return 0;
}
函数是一块代码,接收0个/多个参数做一件事情,并返回0个/1个值。
可以先想象成数学中的函数y=f(x)。
调用函数时要写出 函数名(参数值);
()起到了表示函数调用的重要作用,即使没有参数我们也需要()。
不给括号的话,会给warning,而且函数不会被调用。
如果有参数,参数的数量、顺序都必须正确。而且函数知道每一次是哪里调用它,还会返回到正确的地方。
int
函数会有返回值return
return
停止函数的执行,并返回一个值
return;
return 一个值;
可以写c=函数()
;这样c=函数的返回值。
可以把这个值赋给变量/传给函数/丢掉。
没有返回值的函数:void
,不能使用带值的return
,可以没有return
。调用的时候也不可以做返回值的赋值。
使用函数的先后顺序:先写函数再调用。
可以先想象成数学函数y=f(x)
C的编译器自上而下分析你的代码,它会记住函数sum()
长什么样子要几个参数,每个参数的类型如何,返回什么类型
如果把函数放在后面,C语言可能会猜,你在main
函数里使用的这个函数是什么类型。如果后面发现后面的函数类型和它猜测的不符,就输出error
。
因此也可以先不用写完函数,光把一句函数头放到前面编译也能通过。
(事先声明了函数的样子)
下面函数还会判断一下和你之前的声明是否一致
void sum(int begin,int end);//声明
int main()
{
//略去不表,其中用到了sum(a,b)
}
void sum(int begin,int end)//定义
{
int i,sum=0;
for(i=begin;i<=end;i++){
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
函数头以分号结尾,就构成了函数的原型;
在函数里定义的参数类型与输入的变量的类型不一样,会发生自动类型转换。
double max(double a,double b);
在以前是把函数原型写在调用它的函数(main())里面的。
函数的原型里可以不写参数名,但一般仍然写上。
Void sum(int ,int)
不过对于人类读者,一般来说写上会更好理解。
传递给函数的值可以是表达式的结果(max(a+b,c))
包括字面量、变量、函数返回值(函数调用里有函数)、计算结果
当调用函数的值与参数类型不匹配:编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的。这是C语言传统上最大的漏洞。
后续语言如c++/java在这方面会很严格。
这样的函数代码能交换a,b的值吗?
void swap(int a,int b)//形参
int main()
{
int a=5;
b=6;
swap(a,b);//实参
}
void swap(int a,int b)//形参
{
int t=a;
a=b;
b=t;
}
不能。
C语言在调用函数时,永远只能传值给函数。
每个函数有它自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系。
后面递归还会再提。
过去对于函数参数表中的参数叫做形式参数。调用函数时给的数值叫做实际参数。
(见上图)
由于易误会,我们不再这么称呼。我们把参数表中叫做参数,调用函数的叫做值。
函数每次运行都会产生一个独立的变量空间,其中的变量是函数这一次运行所独有的。(本地变量)
所有我们定义在函数内部的变量就是本地变量。(我们现在学过的都是定义在函数内部的)我们写在函数参数表里的参数也是本地变量。
生存期:这个变量出现和消亡的时间
作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
对于本地变量,这两个答案都是:大括号内(块)
void swap(int a,int b)//形参
int main()
{
int a=5;
b=6;
swap(a,b);//实参
}
void swap(int x,int y)//形参
{
int t=x;
x=y;
y=t;
}
比如仍然是上例,进入swap函数之后(离开了自己的变量空间)a,b就没了。还在生存,但是不在当前的作用域了(显示:Not found in current context)
而回到原函数中之后,x,y,t就不存在了
所以在swap函数里交换a,b不会影响到原函数
本地变量的规则
main()
是啥函数没有参数的时候写void f(void)。而写void f()在传统C语言中表示f的函数的参数表未知,并不表示没有参数。(编译器可能会猜测什么类型)
所以不要写空的括号
调用函数时的逗号和逗号运算符怎么区分?再加一层括号就是逗号运算符f((a,b))
C语言不允许函数嵌套定义。
(也最好不要写return (i);虽然意思上没变,但是会让人误会return 是个函数)
int main(void)
也是个函数,所以return 0;
也是有意义的。