函数
主要是为了封装代码,减少重复
1.主函数:一个项目或者一个工程都是有且仅有1个主函数,程序的运行都是从main函数开始,终止于 main 函数;
2.自定义子函数:即自己写的函数用来实现某些特定功能;
一般,默认自定义子函数在主函数后面编写,此时使用子函数时,需要在main函数前声明,但如果子函数在main前,可不声明。
一.自定义子函数
函数的定义 -- 函数实现功能的整个过程;
函数的声明 -- 告诉编译器,该函数存在;
函数的调用 -- 函数真正的运行;
#include <stdio.h>
int Fun(int a,int b);//函数的声明,告诉编译器,该函数存在
int main()
{
int a = 12,b = 13,sum = 0;
sum = Fun(a,b);//函数的调用,实现定义函数的真正运行
printf("sum=%d\n",sum);
return 0;
}
//函数的定义,定义函数实现功能的过程
int Fun(int a,int b)
{
int sum=a+b;
return sum;
}
1.函数的定义:
格式:返回值类型 函数名(形参类型 形参名,形参类型 形参名……)//函数头
{
函数体;
return 返回值;
}
//函数的定义,定义函数实现功能的过程
int Fun(int a,int b)//返回值类型:int,函数名:Fun, 形参列表:(int a,int b),
{
int sum=a+b;//函数体,实现需要的功能:求和
return sum;//返回sum,sum的数据类型为int,所以定义的函数的返回值类型也为int
}//若无返回值,可以用void,即void 函数名 (形参列表)
函数名:遵循标识符命名规则;驼峰式:Dht11_Init
返回值类型:和return后面的具体的返回值保持一致即可;无返回值:返回值类型写void;(注:返回值只能返回一个数!)
形参:函数在定义的时候写在函数名后面的小括号种的参数。(注:如果没有形参,用void或者不写;形参类型和形参名必须一一对应;形参列表也叫做定义变量,在形参列表定义过的变量,直接在函数体使用即可,不能再重复定义)
2.函数的声明
格式:函数头;//告诉编译器,该函数存在,注意分号
位置:放在调用它的函数的上面,一般统统放在main函数的上面;
int Fun(int a,int b);//函数的声明,告诉编译器,该函数存在
int main()
{
……
return 0;
}
3.函数的调用
格式:返回值类型 变量名=函数名(实参);
int main()
{
int a = 12,b = 13,sum = 0;
sum = Fun(a,b);//函数的调用,实现定义函数的真正运行
printf("sum=%d\n",sum);
return 0;
}
返回值类型和等号后面函数的返回值类型保持一致;
变量名,符合标识符命名规则;
函数名,调用的函数的函数名;
实参,函数被调用的时候,括号里面的提供的叫实参。(注:如果没有形参,实参就不用提供,什么都不写;实参可以是变量,表达式,常量,地址,数组;实参不能写数据类型)
二.形参和实参:
- 形参的起始值来自于实参
- 实参的个数和位置必须和形参一一对应,传参也按照对应位置传递
- 形参和实参各自占用不同的内存空间
- 形参名和实参名可以一样,也可以不一样,各自是各自的局部变量
函数调用的其他格式:
有参有返: 返回值类型 变量 = 函数名(实参);
有参无返: 函数名(实参);
无参有返: 返回值类型 变量 = 函数名();
无参无返: 函数名();
(注意:子函数执行:只有被调用,才能执行,并且只有被调用计算机才分配空间:子函数执行完,空间会被释放!)
三.函数传参:
1.值传递:
形参的起始值来自于实参,形参的变化不会引起实参的改变
形参和实参各自占用不同的内存空间
#include <stdio.h>
void Swap(int a,int b);//声明
int main()
{
int a=10,b=99;
printf("main:a = %d,b = %d\n",a,b);
//a = 10,b = 99
Swap(a,b);//调用
printf("调用后main:a = %d,b = %d\n",a,b);
//a = 10,b = 99;
return 0;
}
void Swap(int num1,int num2)//定义
/* num1和num2仅仅起始值来自于主函数的a和b,和主函数的a和b占据不同的存储空间,改变子函数的num1和num2的值,主函数的a和b不会随着改变*/
{
int tmp = num1;
num1 = num2;
num2 = tmp;
printf("Swap:num1 = %d,num2 = %d\n",num1,num2);
return;
}
运行结果:
main:a = 10,b = 99
Swap:num1 = 99,num2 = 10
调用后main:a = 10,b = 99
2.地址传递:
传递变量的地址,通过指针,改变指针指向的空间内容!
#include <stdio.h>
void Swap(int *p,int *q);
int main()
{
int a=10,b=99;
printf("main:a = %d,b = %d\n",a,b);
Swap(&a,&b);
printf("调用后main:a = %d,b = %d\n",a,b);
return 0;
}
void Swap(int *p,int *q)
{
//p里面存的是a的地址,*p直接找到a,改变*p空间的内容就可以改变a
//q里面存的是b的地址,*q直接找到b,改变*q空间的内容就可以改变b
int tmp = *p;
*p = *q; //改变*p空间的内容就可以改变a
*q = tmp;//改变*q空间的内容就可以改变b
return;
}
运行结果:
main:a = 10,b = 99
调用后main:a = 99,b = 10
数组传参:属于地址传递,写数组名->数组名表示数组的首元素地址
#include <stdio.h>
int Sum(int *p);
int main()
{
int num[6] = {1,2,3,4,5,6};
int a = Sum(num);//实参不写数据类型,数组名=首元素地址
printf("数组的和:%d\n",a);
return 0;
}
int Sum(int *p)//int *p=num;传递num的首地址
{
int sum = 0;
for(int i=0;i<6;i++)
{
sum += *(p+i);
}
return sum;
}
(子函数的数组相当于局部变量,当调用完后,子函数的空间被释放,引入static)
被static修饰的局部变量:和全局变量一样,从定义处开始,到当前函数结束;
#include <stdio.h>
int *Fun(void);
int main()
{
int *p = Fun();
for(int i=0;i<6;i++)
{
printf("%d\n",*(p+i));
}
return 0;
}
//子函数只有被调用,才分配空间,使用完,空间会被释放!
int *Fun(void)
{
static int num[6] = {1,2,3,4,5,6};//static 修饰,延长生命周期,类似全局变量
return num;//数组名是首元素地址 &num[0],而num[0]的数据类型为:int *,所以Fun前的数据类型为int *
}
(全局变量,定义在函数体外部的变量,从定义处开始,到当前文件.c结束;局部变量,定义在函数体内部的变量,从定义处开始,到当前函数结束)
指针函数:返回值为指针的函数
函数指针:指向函数的指针