当前位置: 代码迷 >> 综合 >> 跟着小甲鱼学C——Lesson6
  详细解决方案

跟着小甲鱼学C——Lesson6

热度:18   发布时间:2024-02-10 19:19:52.0

函数

  • 目前我们在开发中遇到的问题

随着程序规模的变大

——main函数变得相当冗杂

——程序复杂度不断提高

——代码前后关联度高,修改代码往往牵一发而动全身

——变量的命名都成了问题

——为了在程序中多次实现某功能,不得不重复多次写相同的代码

——...

函数的出现就解决了上述问题

先动手,再解释

#include<stdio.h>void printf_C();//函数的声明;注意有分号void printf_C()  //函数的定义
{printf(" ###### \n");printf("##    ##\n");printf("##      \n");printf("##      \n");printf("##    ##\n");printf(" ###### \n");
}
int main()
{printf_C();//函数的调用;调用几次就打印几次return 0;} 
编译结果:######
##    ##
##
##
##    ########
  • 什么是函数?

一段封装的代码,实现了某一个功能

  • 函数的定义

类型名  函数名(参数类型  参数)

      函数体

      return 返回值;

注:类型名是函数的返回值的类型;如果不返回任何值,那么就写void;如果没写,默认返回整型。

函数名尽量以实现的功能来命名,一目了然。

参数列表:

  • 函数的声明

在函数定义前调用函数的话,要加声明。

所谓声明,就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把它定义上。

但是最好按照声明,定义,调用的顺序编写,一目了然。

  • 返回值

返回值:函数定义的时候声明函数返回值类型,函数结束前返回一个值

注意:

1、函数声明中可以只写  参数类型  ,不写形参名字,但是定义不行

2、函数参数名如果有,要和定义保持一致(不一致可能会出现问题)

3、void类型函数可以没有返回值

  • 形参和实参

形参:定义或者声明时的参数为形式参数,简称形参

实参:函数被调用时的参数为实际参数,简称实参

 

说白了形参和实参的功能就是用于数据传输,当函数发生调用的时候,实参的值会传递给形参,

函数传参:形参就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。

 

当调用函数时,有两种向函数传递参数的方法:

(1)传值调用

该方法把参数的实际值复制给函数的形参。在这种情况下,修改函数内的形参不会影响实参。

例:互换两个变量的值:

#include<stdio.h>void swap(int,int);void swap(int x,int y)
{int temp;printf("In void,互换前:x = %d,y = %d\n",x,y);temp = y;y = x;x = temp;printf("In void,互换后:x = %d,y = %d\n",x,y);
}
int main()
{int x = 3,y = 5;printf("In main,互换前:x = %d,y = %d\n",x,y);swap(x,y);printf("In main,互换后:x = %d,y = %d\n",x,y);return 0;
}
编译结果:
In main,互换前:x = 3,y = 5
In void,互换前:x = 3,y = 5
In void,互换后:x = 5,y = 3
In main,互换后:x = 3,y = 5

这里由于没有返回值,所以在主函数中尽管调用了swap函数,x和y的值在主函数中也没有改变。

(2)传址调用

通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作

例:还是互换两个变量的值,用传址

#include<stdio.h>void swap(int *x,int *y);void swap(int *x,int *y)
{int temp;printf("In void,互换前:x = %d,y = %d\n",*x,*y);temp = *y;*y = *x;*x = temp;printf("In void,互换后:x = %d,y = %d\n",*x,*y);
}
int main()
{int x = 3,y = 5;printf("In main,互换前:x = %d,y = %d\n",x,y);swap(&x,&y);printf("In main,互换后:x = %d,y = %d\n",x,y);return 0;
}
编译结果:
In main,互换前:x = 3,y = 5
In void,互换前:x = 3,y = 5
In void,互换后:x = 5,y = 3
In main,互换后:x = 5,y = 3

这里是把实参即变量x和y的地址传给形参两个指针变量——传址

与传值不同的是main函数中x和y的值也随着互换,因为传的是地址,而地址是唯一的,故形参地址对应的值变了,实参也跟着变。

----------

例1:编写一个函数sum,由用户输入参数n,计算1+2+3+...+n的结果并返回

#include<stdio.h>int sum(int n);int sum(int n)//形参 
{int result = 0;do{result += n;}while(n-- >0);return result;
}
int main()
{int n,result;printf("请输入n的值:\n");scanf("%d",&n);result = sum(n);//实参 printf("1+2+3+...+n = %d\n",result);return 0;
}
编译结果:
请输入n的值:
100
1+2+3+...+n = 5050

例2:编写一个函数max,接收两个整形参数,并返回他们中较大的值。

#include<stdio.h>int max(int ,int);//声明时参数可以不写 int max(int x,int y)
{if(x > y)return x;elsereturn y;
}int main()
{int a,b,c;printf("请输入两个整数:\n");scanf("%d%d",&a,&b);c = max(a,b);printf("他们中较大的是:%d\n",c);return 0;
}
编译结果:
请输入两个整数:
8 23
他们中较大的是:23

注:形参和实参符号可以相同也可以不同,互不影响的,因为各个函数内部是独立的。

---------

  • 传数组

例:

#include<stdio.h>int get_array(int a[10]);
int get_array(int a[10])
{int i;for(i = 0;i < 10;i++){printf("a[%d] = %d\n",i,a[i]);}
}
int main()
{int a[10] = {1,2,3,4,5,6,7,8,9,0}; get_array(a);return 0;	
}
编译结果:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
a[5] = 6
a[6] = 7
a[7] = 8
a[8] = 9
a[9] = 0

由实参传向形参的是整个数组吗?

答案是否定的,传递的仅仅是数组的第一个元素的地址(数组名就是数组第一个元素的地址)

证明:

#include<stdio.h>int get_array(int a[10]);int get_array(int a[10])
{int i;a[5] = 520;for(i = 0;i < 10;i++){printf("a[%d] = %d\n",i,a[i]);}
}
int main()
{int a[10] = {1,2,3,4,5,6,7,8,9,0}; int i;get_array(a);for(i = 0;i < 10;i++){printf("a[%d] = %d\n",i,a[i]);}return 0;	
}
编译结果:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
a[5] = 520
a[6] = 7
a[7] = 8
a[8] = 9
a[9] = 0
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
a[5] = 520
a[6] = 7
a[7] = 8
a[8] = 9
a[9] = 0

可以看到当修改get_array中数组元素a[5]的值时,main函数中a[5]的值也跟着一起改变,也就验证了传递的仅仅是地址。

也可以通过这个程序验证:

#include<stdio.h>void get_array(int b[10]);void get_array(int b[10])
{printf("sizeof(b) = %d\n",sizeof(b));
}
int main()
{int a[10] = {1,2,3,4,5,6,7,8,9,0};printf("sizeof(a) = %d\n",sizeof(a));get_array(a);return 0;} 
编译结果:
sizeof(a) = 40
sizeof(b) = 8

指针函数和函数指针

  • 指针函数

格式:函数返回值类型   *指针变量名(参数类型   参数 );

例:

#include<stdio.h>char *geteword(char);char *getword(char input)//函数名表明返回值是一个地址
{switch(input){case 'A':return "Apple";case 'B':return "Banana";case 'C':return "Cat";case 'D':return "Dog";default:return"None";}
}
int main()
{char input;printf("请输入一个字母:\n");scanf("%c",&input);printf("%s\n",getword(input));//返回的是字符串首字符的地址,用%s就可以把整个字符串打印出来return 0;} 
编译结果:
请输入一个字母:
A
Apple

main函数中要从getword函数中获得的是一个字符串,也就是要求getword函数返回值是一个字符串;

但是是没有一个类型来定义字符串的,通常来用char 类型的指针来定义字符串,即用一个char类型的指针变量来指向字符串的第一个元素,这时返回的就是这个字符串的第一个字符的地址。

 

像这样用指针变量作为函数的返回值,就是指针函数。

 

注意:不要返回局部变量的指针!

#include<stdio.h>char *geteword(char);char *getword(char input)
{char str1[] = "Apple";//这里定义str1是局部变量,用完即毁char str2[] = "Banana";char str3[] = "Cat";char str4[] = "Dog";char str5[] = "None";switch(input){case 'A':return str1;//自然无法返回到main函数case 'B':return str2;case 'C':return str3;case 'D':return str4;default:return str5;}
}
int main()
{char input;printf("请输入一个字母:\n");scanf("%c",&input);printf("%s\n",getword(input));return 0;} 
编译结果:
有警告——[Warning] function returns address of local variable(函数返回了局部变量)
但还是编译了
请输入一个字母:
A
Apple

对比之前的程序知道,直接在函数中返回字符串的首地址是可行的,因为这个字符串没有定义在这个函数中,也就是不是局部变量。

 

  • 函数指针

(1)什么是函数指针

如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

(2)格式:函数返回值类型    (*指针变量名)( 函数参数列表);

”函数参数列表”表示该指针变量可以指向具有什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可。

(3)怎么用?

例:

#include<stdio.h>int square(int);int square(int num)
{return num*num;
}
int main()
{int num;int (*fp)(int);                  //定义函数指针 printf("请输入一个整数:");scanf("%d",&num);fp = square;                     //将square函数的首地址(即函数名)赋给指针变量fpprintf("%d * %d = %d\n",num,num,(*fp)(num));//即square(num)return 0;
}
请输入一个整数:8
8 * 8 = 64

注:printf("%d * %d = %d\n",num,num,(*fp)(num));中,(*fp)(num)也可以写成fp(num)或square(num),但是(*fp)(num)可以直观看出这是一个函数指针,推荐。

  • 函数指针作为参数

例:

#include<stdio.h>//函数声明 
int add(int,int);
int sub(int,int);
int cacl(int (*fp)(int,int),int,int);//函数定义 
int add(int num1,int num2)
{return num1 + num2;
}int sub(int num1,int num2)
{return num1 - num2;
}int cacl(int (*fp)(int,int),int num1,int num2)
{return (*fp)(num1,num2);      //返回函数指针指向的函数地址
}//函数调用 
int main()
{printf("3 + 5 = %d\n",cacl(add,3,5));printf("3 - 5 = %d\n",cacl(sub,3,5));return 0;
}
编译结果:
3 + 5 = 8
3 - 5 = -2

事实上就是一层函数的嵌套;

int cacl(int (*fp)(int,int),int,int);

//函数指针作为函数cacl的一个参数,这个参数可以传入一个地址,这个地址具有函数指针的属性(返回一个整型值,有两个参数,且两个参数的类型都是整型),故这个指针指向add或sub