C语言(六)函数

本文围绕C语言展开,介绍了逻辑类型、逻辑运算、条件运算符等内容。逻辑运算结果为0或1,有短路特性。还阐述了函数相关知识,包括定义、调用、返回值、原型、参数传递和本地变量等,强调函数可避免代码重复,本地变量有其生存期和作用域。

6.2.1 逻辑类型:表示关系运算和逻辑运算结果的量

bool类型
首先要包含头文件#include<stdbool.h>,然后可以使用bool,true,false。

bool b=6>5;

只要bool量不是0,都=1.

6.2.2 逻辑运算:对逻辑量进行与、或、非运算

逻辑运算是对逻辑量进行的运算,结果只有0或1。
逻辑量是关系运算或逻辑运算的结果。

运算符描述示例结果
!逻辑非!aa的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<6x>=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不会影响到原函数
本地变量的规则

  1. 本地变量定义在块内。可以是函数的块内,语句的块内,如:
    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;也是有意义的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰海宽松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值