基本概念
- c 语言是由函数组成的,虽然之前只看到了 main 函数,但函数是 c 语言的基本模块.通过对函数模块的调用实现不同的功能
- C语言不仅提供了三百多个系统函数,还允许自己定义函数,使用的时候再进行调用
函数的分类
-
根据函数定义的角度:函数分为库函数和用户自定义函数stdio.h stdlib.h
- 库函数:由系统提供,用户无需定义也不必咋及程序中做类型说明,只需要在程序钱包含有该函数原型的头文件即可在程序中直接调用.在前面学习甬道的 printf,scanf,getchar,putchar,gets,puts,strcat 等函数均属于此类
- 用户定义函数:由用户按需要写的函数,对于用户自定义函数不仅要在程序中定义函数本身,而且在主调函数模块中还必须对盖被调用函数进行类型说明,然后才能使用
-
C语言的函数兼有其他语言中的函数和过程两种功能,从这个角度看,又可以把函数分为有返回值函数和无返回值函数
- 有返回值函数:此类函数被调用执行完后调用者返回一个执行结果,成为函数返回值,如数学函数即属于此类函数,由用户定义的这种要返回函数值得函数,必须在函数定义和函数说明中明确返回值的类型
- 无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值,这类函数类似于其他语言的过程,由于函数无须返回值,用户在定义此类函数是可指定它的返回值为"空类型"空类型的说明符为"void"
-
从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数
- 无参函数:函数说明及函数低啊用中局部带参数,主调函数和被调函数之间不进行参数传递,此类函数通常用来完成一组制定的功能,可以返回或不返回函数值
- 有参函数:也成为带参函数,在函数定义的函数说明时都有参数,成为形式参数,简称形参,在函数调用是必须给出参数,称为实际参数简称实参,进行函数滴啊用时,主调函数讲吧实参的只传送给形参,供被调函数使用
-
main 函数:main 是函数的主函数,他可以调用其他函数而不允许被其他函数调用,因此,C 程序的执行总是从 main 函数开始,完成对其他函数的调用后再返回到 main 函数,最后由 main 函数结束整个程序,一个 c 源程序必须有也只能有一个主函数 main
函数的定义
int getMax(int a ,int b);// 自定义的函数在 main 函数后面所以需要先声明一下,不然找不到定义的函数
int main(int argc, const char * argv[]) {
/*
函数的定义格式:
返回值类型 函数名称(形参列表){
函数体;
}
*/
/*
初学者如何定义函数呢?
1,确定函数名称(给函数起一个有意义的函数名称,让调用者一看到名称就知道这个函数是干什么用的)
1.1 函数名称由数字,字母,下划线组成,
2.1 遵守驼峰式命名
2,确定形参列表
2.1形参列表的格式:(数据类型 变量名称....)
2.2如何确定形参列表,就看以后调用函数时是否需要传递一些辅助的数据给函数
3,确定返回值
3.1return 有两个作用(1,返回数据给调用者.2,用来结束函数)
3.2确定返回值类型:你 return 的是什么e类型,那么返回值的类型就是什么类型
*/
// 要求获取两个数的最大值
int a = 10;
int b = 20;
// int maxNum = a > b ? a:b;
// printf("最大值:%i
",maxNum);
// 通过函数来实现获取两个数的最大值
int maxNum = getMax(a,b);
printf("最大值:%i
",maxNum);
return 0;
}
int getMax(int a ,int b){
int maxNum = a > b?a:b;
return maxNum;
}
函数的调用过程
int average(int a ,int b);// 声明求平均值的函数
int main(int argc, const char * argv[]) {
// 求两个数的平均值
int a = 10;
int b = 20;
int result = average(a, b);
printf("平均值:%i
",result);
return 0;
}
int average(int a ,int b){
int averageNum = (a+b)/2;
return averageNum;
}
-
1,程序执行先找 main 函数,
-
2,main 函数中发现 int a = 10; 此时在内存中开辟一块放 a并初始化为 10,int b = 20; 此时又在内存中开辟一块放 b 并初始化值为 20
-
3,int result = average(a,b);发现函数 vaerage 此时跳到函数 average 开辟一个内存空间用于 average 函数
-
4,在 average 函数中发现 int a ; 此时在内存中开辟一块放 a,int b; 此时又在内存中开辟一块放 b ,此时这里的值是由实参传递过来了 a=10,b=20;
-
5,又定义了一个 averageNum,在内存中开辟一块空间用于存放 averageNum 的值,并计算了等号右边的值为 15,赋值诶 averageNum.
-
6,此时在averageNum 中遇到了 return,此时将 averageNum 的值替换average(a,b),并且结束函数,此时 average 的内存空间被销毁
-
7,在 main 函数中遇到 printf 函数将 result 的值进行打印
-
注意:
- 形参列表中的变量只有当函数被调用时才会分配内存空间
- 形参列表中每一个变量我们称之为形式参数简称:形参
函数的注意点
-
在函数中修改形参的值,不会影响到外面实参的值(当然也有一定的前提,目前只有基本数据类型不会影响,数组是会受到影响的)
-
如果基本数据类型(char,int,float,double)作为函数的形参,在函数中修改形参的值是不会影响到外面实参的值
-
实参的参数个数必须与形参的个数一致
-
当我们的返回值与返回值类型不一致时,系统会对返回值进行自动类型转换(隐式类型转换)
- 企业中开发千万不要这么用
int height(){
return 17.77; // 返回值类型为 int,但是 return 返回值为 double 类型,所以做了自动类型转换
}
main(){
int result = height();
printf("返回值与返回数据类型不一致时会进行自动类型转换:%i
",result);//17
}
- 4,函数可以有形参也可以没有形参
- 5,函数可以有返回值也可以没有返回值,也可以写 rerurn;
// 打印一朵玫瑰花
void printRose(){
printf("{@}
");
printf(" |
");
printf("\|/
");// 是一个转义字符,需要再次转义才可以正确输出
printf(" |
");
}
main(){
printRose();
}
-
6,rerurn 语句后面不能写任何的语句,因为执行不到比如:break,continue
-
7,函数名称不能相同
- 形参不同不可以
- 返回值类型不一样不可以
-
8,函数不能嵌套定义
void test(){ vodi test1(){ printf("这样是不行的"); } }
-
9,函数如果没写返回值类型,那么默认是 int类型
rerutn的应用场景
-
1,用于在函数中返回结果给调用者
-
2,结束函数
-
3,提前结束函数
认证:比如登录 QQ
login(){ // 1,用户只输入了账号没有输入密码 // 2,用户只输入了密码但没有输入账号 // 那么上面两种情况是不是不用发送网络请求 if(账号 == NULL){ return; } if(密码 == NULL){ return; } // 发送网络请求到服务器,将用户名和密码上传 }
练习
- 打印 n 朵玫瑰花
// 打印 N 朵玫瑰花
void printRose(){ // 打印一朵玫瑰花
printf("{@}
");
printf(" |
");
printf("\|/
");// 是一个转义字符,需要再次转义才可以正确输出
printf(" |
");
}
void prinfRoseN(int n){ // 打印 n 朵玫瑰花
for (int i = 0; i < n ; i++) {
printRose();
printf("第%i 朵玫瑰花
",i+1);
}
}
main(){
prinfRoseN(10);//调用
}
- 打印三个数的最大数
int getThereNum(int a ,int b,int c){
int temp;
temp = a > b ? a:b;
int result = temp > c ? temp:c;
return result;
}
main(){
// 打印三个数的最大值
int a = 10;
int b = -20;
int c = 0;
int result = getThereNum(a, b, c);
printf("三个数的最大值:%i
",result);
}
函数的声明和实现
- 以前我们在定义函数的时候,一直都是把函数定义在 main 函数之前
- C语言规定:一般情况下如果想要调用一个函数,该函数必须在 调用之前出现
mian(){
int a = 10;
int b = 20;
int result = average(a,b); // 此时会报错,错误原因是因为c 语言的执行顺序从上到下,在 main 函数中出现了函数 average,但是在 main 函数之前呢并没有这个函数,这个函数写在了 main 函数之后,所以引出了函数的声明
printf("两个数的平均值是:%i
",result);
}
// 完整的实现了函数的功能,称之为函数的定义
int average(int a ,int b){
int averageNum = (a+b)/2;
return averageNum;
}
-
函数声明的作用;
-
在调用函数之前告诉系统有这个函数,并且告诉系统,该函数叫什么名字,接收什么参数,返回什么类型的值
-
声明怎么写呢?就是把函数实现的大括号之前的内容拷贝到 main 函数之前
-
int average(int a ,int b) // 函数的声明
mian(){
int a = 10;
int b = 20;
int result = average(a,b);
printf("两个数的平均值是:%i
",result);
}
int average(int a ,int b){
int averageNum = (a+b)/2;
return averageNum;
}
- 注意:如果函数的返回值是 int 类型的话,可以不用写声明,函数也不会报错,其他返回值类型会直接报错(float,char,double,void).但还是建议无论那种类型都还是写上声明
- 声明的第二种写法
int average(int ,int ) // 此时可以省略掉形参,只写形参的值的类型即可,因为根本用不到这个形参,函数的声明只是告诉系统有这个函数
mian(){
int a = 10;
int b = 20;
int result = average(a,b);
printf("两个数的平均值是:%i
",result);
}
int average(int a ,int b){
int averageNum = (a+b)/2;
return averageNum;
}
- 声明的第三种写法(声明可以写到 函数外面也可以写在函数里面,只要是调用函数之前就可以)
mian(){
int a = 10;
int b = 20;
int average(int ,int); // 函数声明
int result = average(a,b);
printf("两个数的平均值是:%i
",result);
}
int average(int a ,int b){
int averageNum = (a+b)/2;
return averageNum;
}
- 函数名是不可以重复定义(实现)同一个函数,但是声明是可以重复的
为什么把函数的定义写在 main 函数之后不写在main 函数前面呢?
-
如果定义的函数写在了前面,还有很多定义的函数,那么 main 函数是不是非常靠后,那么别人在阅读你的代码的时候就要写阅读你的定义函数.非常耽误时间
-
我们在写东西的时候要把重要的东西写到前面,这样别人一看就知道在干嘛
main 函数分析
// main是函数的名称,系统规定的名称,系统启动就会调用 main 函数
// main 函数只能有程序调用,不能手动调用
// 其中系统调用的时候会给我们传递一些参数
// argc:就是系统传递进来的参数个数, 默认是 1
// argv:系统传递进来的参数实际的值,默认是程序的名称
main(int argc,const char * argv[]){
// return结束函数
// 0 返回给操作系统的,返回 0 代表程序正常结束,如果返回其他值代表程序非正常结束
return 0;
}
- 如何给 main 函数传递参数