四、函数
<1>为什么需要函数
*避免了重复性操作
*有利于程序的模块化
举个例子:
#include<stdio.h>
/*int main(void)
{
int a,b,c,d,e,f;
a=1,b=2,c=3,d=9,e=-5,f=100;
if(a>b)
printf("%d\n",a);
else
printf("%d\n",b);
if(c>d)
printf("%d\n",c);
else
printf("%d\n",d);
if(e>f)
printf("%d\n",e);
else
printf("%d\n",f);
return 0;
}*/
void max(int i,int j)//max是函数的名字,i和j是形式参数(形参),void表示函数无返回值
{
if(i>j)
printf("%d\n",i);
else
printf("%d\n",j);
}
int main(void)
{
int a,b,c,d,e,f;
a=1,b=2,c=3,d=9,e=-5,f=100;
max(a,b);
max(c,d);
max(e,f);
return 0;
}
<2>什么叫函数(函数是个工具,它是为了 解决大量类似的问题而设计的)
*逻辑上:能够完成特定功能的独立代码块
*物理上:能够对接收的数据进行处理
能够将处理的数据进行返回
(注意:定义和调用函数的名称要一致!!!)
*举个例子:
#include<stdio.h>
int f(void)//括号中的void表示该函数不能接收数据,int表示函数返回值是int类型
{
return 10;//虽然返回值为10,但是此函数无法接收数据!!
}
void g(void)
{
return;//函数名前的void表示该函数无返回值,如果写了返回值则会出现警告!!
}
int main(void)
{
f();
g();
return 0;
}
//此程序无输出值
<3>如何定义函数
*格式:
函数的返回值类型 函数的名字(函数的形参列表)
{
函数的执行体
}
*函数定义的本质是详细描述函数之所以能实现某个特定功能的具体方法
*关于return表达式的一些问题:
函数返回值的类型也称函数的类型,若函数名之前的返回值类型和函数执行体中的return表达式;中表达式的类型不同的话,则最终函数返回值的类型以函数名前返回值的类型为准
*举个例子:
#include<stdio.h>
int f()//函数返回数据类型主要定义前的为准
{
return 10.5;//最终只会返回10而不会返回10.5
}
int main(void)
{
double x;
x=0.6;
x=f();
printf("%lf\n",x);
return 0;
}
*return和break的区别
简单来说:return是回来终止函数的
break终止循环和Switch的
举个例子:
#include<stdio.h>
void f(void)
{
int i;
for(i=0;i<5;i++)
{
printf("哈哈!\n");
return;//break;
}
printf("嘿嘿!\n");
}
int main(void)
{
f();
return 0;
}
//break输出:哈哈!嘿嘿!
//return输出:哈哈!
总结return的作用:
a、终止被调函数,向主调函数返回表达式的值
b、如果表达式为空,则会终止函数,不向被调函数返回任何值
即:
#include<stdio.h>
void f()
{
return;//终止函数,不向被调函数返回任何值
}
int f()
{
return 10;//终止函数,向主调函数返回10
}
<4>函数的分类
*有参函数和无参函数
*有返回值函数和无返回值函数
*库函数和用户自定函数
*普通函数和主函数
*值传递函数和地址传递函数
【Tip】
*一个程序有且只能有一个主函数
*主函数可以调用普通函数,普通函数无法调用子函数
*普通函数可以相互调用
*主函数是程序的入口,也是程序的出口
<5>举个例子:
*判断一个数字是否为素数
1、#include<stdio.h>
#include<stdbool.h>//调用库函数bool需要添加一个头文件
bool IsPrime(int val)//IsPrime是素数的英文
{
int i;
int flag = 0;//立一个旗子,通过验证数字的变化进行判断
for(i=2;i<val;++i)
{
if(val%i==0)
{
flag=-1;
break;
}
}
if(flag==-1)
return false;
else
return true;
}
int main(void)
{
int m;
scanf("%d",&m);
if(IsPrime(m))//m进入自定义函数的内部
printf("Yes!\n");
else
printf("No!\n");
return 0;
}
2、#include<stdio.h>
int IsPrime(int val)
{
int i;
for(i=2;i<val;++i)//i若能加到val,说明他是素数,若加不到则说明不是
{
if(val%i==0)
break;
}
if(i==val)//输入3,则i需要加1变成3,此时i=val=3
//输入4,则i不变,for循环直接被break掉,val=4,i=2
return 0;
else
return 1;
}
int main(void)
{
int m;
scanf("%d",&m);
if(IsPrime(m))
printf("Yes!\n");
else
printf("No!\n");
return 0;
}
*利用Fact函数计算阶乘之和
#include <stdio.h>
unsigned long Fact(unsigned int n);
int main()
{
unsigned int i, n;
unsigned long sum = 0;
printf("Input n(n>0):");
scanf("%u", &n);
for (i = 1; i <= n; i++)
{
sum = sum + Fact(i);
}
printf("sum = %lu\n", sum);
return 0;
}
/* 函数功能:用迭代法计算无符号整型变量n的阶乘 */
unsigned long Fact(unsigned int n)
{
unsigned int i;
unsigned long result = 1;
for (i = 2; i <= n; i++)
result *= i;
return result;
}
<6>函数的声明
*【如果主函数在调用其他自定义函数的时候没放在其后面,则需要在最前面声明函数】
*举个例子:
#include<stdio.h>
void f(void);//这就是函数声明,切记分号不能丢!!!
int main(void)//如果不加这个声明则会语法出错
{
f();
return 0;
}
void f(void)
{
printf("哈哈!\n");
}
*函数调用和函数定义的顺序
如果函数调用写在了函数定义的前面,则必须加函数的前置声明
函数的前置声明
a、告诉编译器即将可能出现的若干个字母代表的是一个函数
b、告诉编译器即将可能出现的若干个字母所代表的函数的形参和返回值的具体情况
c、函数声明是一个语句,末尾必须加分号
d、对库函数的声明是通过#include<库函数头文件的名字.h>
<6>形参和实参
void f(int i)
{
printf("%d\n",i);
}
int main(void)
{
f(5);
return 0;
}//i为形参,5为实参
【Tip】
*个数相同
*位置一一对应
*数据类型必须相互兼容(输入一个实数整形可以兼容输出,但输入一个字符串,则会报错)
<7>如何在开发中合理设计函数来解决实际问题
【求1到某个数之间所有的素数并输出】
*只使用main函数
#include<stdio.h>
int main(void)
{
int val;
int i;
int j;
scanf("%d",&val);
for(i=2;i<=val;++i)
{
for(j=2;j<i;++j)
{
if(0==i%j)
break;
}
if(j==i)
printf("之间的素数为:%d\n",i);
}
return 0;
}
【只使用main函数实现有局限性,代码的重用性不高】
*加入一个函数
#include<stdio.h>
int IsPrime(int val)//此函数的功能是判断val是否为素数
{
int i;
for(i=2;i<val;++i)
{
if(0==val%i)
break;
}
if(i==val)
return 1;
else
return 0;
}
int main(void)
{
int j;
int val;
scanf("%d",&val);
for(j=2;j<=val;++j)
{
if(IsPrime(j))
printf("之间的素数:%d\n",j);
}
return 0;
}
【此程序可重用性比上一个高,更易理解;但还不是非常高】
比如:求1000个数字,求它们每个数字从1到它本身的素数
则下面的代码要写1000次!!!
for (i=2;i<val;++i)
{
if(IsPrime(i))
printf("%d\n"),i);
}
*再加入一个函数
#include<stdio.h>
int IsPrime(int val)
{
int i;
for(i=2;i<val;++i)
{
if(val%i==0)
break;
}
if(i==val)
return 1;
else
return 0;
}
void f(int n)//此函数的功能是把1到n之间所有的素数结合另一个函数判断并输出
{
int i;
for(i=2;i<=n;i++)
{
if(IsPrime(i))
printf("之间的素数为:%d\n",i);
}
}
int main(void)
{
int val=0;
scanf("%d",&val);
f(val);
return 0;
}
【用两个函数来实现可重用性高,代码量少】
<8>常用的系统函数
*double sqrt(double x);
求x的平方根
*int abs(int x)
求x的绝对值
*double fabs(double x)
求x绝对值
<9>递归
【函数在运行时自己调用自己】
举个例子:从1加到n的和
f(100)=1+2+3+4+...+100
f(n)=1+2+3+4+...+n
f(n)=f(n-1)+n
f(1)=1
#include<stdio.h>
int sum(int n)
{
if(n==1)
{
return 1;
}
else
{
return (sum(n-1)+n);
}
}
int main(void)
{
int num=sum(100);
printf("%d\n",num);
return 0;
}//输出 5050
//利用递归思想求1到100的和
【栈】
栈是一种数据项按序排列的数据结构。栈就像装数据的桶或箱子,它是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取(先进后出,后进先出)
这就如同我们要取出放在箱子里面底下的东西(放入的比较早的物体),我们首先要移开压在它上面的物体(放入的比较晚的物体)
当调用函数时会将数据存放在栈里,每个栈会占用一定的内存空间,当调用完成后这些数据会被释放,即值被返回的过程 (所以函数除非被定义为void类型,一般都需要返回一个值,否则程序会崩溃!!!)
<10>变量的作用域和存储方式
*按作用域分:
全局变量:在所有函数外部定义的变量【使用范围:从定义位置到整个程序结束】
局部变量:在一个函数内部定义的变量或者函数的形参,都统称为局部变量
【使用范围:只能在本函数内部使用】
【注意:全局变量和局部变量命名冲突的问题】
在一个函数内部定义的局部变量的名字和全局变量名字一样时,局部变量会屏蔽掉全局变量
举个例子:
#include<stdio.h>
int i=99;
void f(int i)
{
printf("i=%d\n",i);
}
int main(void)
{
f(8);
return 0;
}//输出结果:8
//证明局部变量会屏蔽掉全局变量
#include<stdio.h>
int i=99;//全局变量在子函数内部也会生效 ,所以变量名称不可以定义成一样的
void f(int i)
{
int i;//一个函数内部不能同时定义两个相同的变量
}