6.2.1 逻辑类型:表示关系运算和逻辑运算结果的量
bool类型
首先要包含头文件#include<stdbool.h>,然后可以使用bool,true,false。
bool b=6>5;
只要bool量不是0,都=1.
6.2.2 逻辑运算:对逻辑量进行与、或、非运算
逻辑运算是对逻辑量进行的运算,结果只有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++。
所以不要把赋值和复合赋值组合进表达式!
6.2.3 条件运算符
条件运算符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--)
目前来说,逗号表达式只有这一个用处。
7.1.1 初见函数
回想起区间[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;
}
7.1.2 函数的定义和调用
函数是一块代码,接收0个/多个参数做一件事情,并返回0个/1个值。
可以先想象成数学中的函数y=f(x)。
调用函数时要写出 函数名(参数值);
()起到了表示函数调用的重要作用,即使没有参数我们也需要()。
不给括号的话,会给warning,而且函数不会被调用。
如果有参数,参数的数量、顺序都必须正确。而且函数知道每一次是哪里调用它,还会返回到正确的地方。
7.1.3 从函数中返回
int
函数会有返回值return
return
停止函数的执行,并返回一个值return;
return 一个值;
可以写 c=函数()
;这样c=函数的返回值。
可以把这个值赋给变量/传给函数/丢掉。
没有返回值的函数:void
,不能使用带值的return
,可以没有return
。调用的时候也不可以做返回值的赋值。
7.2.1 函数原型:用来告诉编译器这个函数长什么样
使用函数的先后顺序:先写函数再调用。
可以先想象成数学函数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)
不过对于人类读者,一般来说写上会更好理解。
7.2.2 参数传递:调用哪个函数的时候,是用表达式的值来初始化函数的参数
传递给函数的值可以是表达式的结果(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语言在调用函数时,永远只能传值给函数。
每个函数有它自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系。
后面递归还会再提。
过去对于函数参数表中的参数叫做形式参数。调用函数时给的数值叫做实际参数。
(见上图)
由于易误会,我们不再这么称呼。我们把参数表中叫做参数,调用函数的叫做值。
7.2.3 本地变量:定义在函数内部的变量是本地变量,参数也是本地变量
函数每次运行都会产生一个独立的变量空间,其中的变量是函数这一次运行所独有的。(本地变量)
所有我们定义在函数内部的变量就是本地变量。(我们现在学过的都是定义在函数内部的)我们写在函数参数表里的参数也是本地变量。
变量的生存期和作用域
生存期:这个变量出现和消亡的时间
作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
对于本地变量,这两个答案都是:大括号内(块)
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不会影响到原函数
本地变量的规则
- 本地变量定义在块内。可以是函数的块内,语句的块内,如:
if (a<b)int i=10;
离开if语句之后未定义使用i编译错误。(没声明)
程序运行进入这个块前,其中的变量不存在。离开后就消失了。
如果在块里定义了块外已经定义过的变量,就把块外的变量掩盖了;出来之后又回到块外的值。(C语言)
但是不能在块里面定义同名变量(多次定义)
本地变量不会被默认初始化,不会得到一个初始值。而参数进入函数的时候被初始化了。
7.2.4 函数庶事:一些细节,main()
是啥
函数没有参数的时候写void f(void)。而写void f()在传统C语言中表示f的函数的参数表未知,并不表示没有参数。(编译器可能会猜测什么类型)
所以不要写空的括号
调用函数时的逗号和逗号运算符怎么区分?再加一层括号就是逗号运算符f((a,b))
C语言不允许函数嵌套定义。
(也最好不要写return (i);虽然意思上没变,但是会让人误会return 是个函数)int main(void)
也是个函数,所以return 0;
也是有意义的。