函数的基本使用:
将特定代码封装成函数的好处:实现代码复用,减少冗余,简化代码
一个c源程序可以由一个或多个源文件构成,一个源文件是一个编译单位,一个源文件由若干个函数构成,函数之间可以相互调用。也就是说。函数是c程序基本的组成单位。
函数的分类:
从程序执行的角度:
主函数:main()函数
子函数:非main()函数
每个c应用程序只有一个,且必须有一个main()主函数,无论主函数写在什么位置,c程序总是从main()函数开始执行,main()函数可以调用其他子函数,子函数之间可以相互调用任意多次
是否允许源文件外调角度:
内部函数
外部函数
从用户角度:
1. 库函数(或标准函数):
由C系统提供的,用户不必自己定义,可以直接使用它们。注意:不同的c系统提供的库函数的数量和功能会有一些差异,但是一些基础的函数是共同的,比如:
字符串操作符
字符操作函数
时间/日期函数
数学函数
IO函数
内存操作函数
其他库函数
使用库函数,必须包含#include对应的文件
2.用户自己定义的函数:它是用以解决用户特定业务需求的函数
函数的声明格式:
返回值类型 函数名(数据类型1 形参1,数据类型2 形参2,……数据类型n 形参n){
函数体;
}
举例:
//定义一个函数,用以计算两个整数的和
int add(int a, int b) {
int c;
c = a + b;
return c;
}
//定义一个函数,计算两个数的较大值,并返回
int max(int a, int b) {
int c;
c = (a > b)?a:b;
return c;
}
//定义一个函数,计算两个整数的较大值,并打印
void PRINTFMax(int a, int b) {
int c;
c = (a > b)?a:b;
printf("%d\n",c);
}
具体说明:
返回值类型:
函数调用后,是否需要在主函数(比如main()函数)中得到一个确定的返回的值,针对这个返回值的描述,就是返回值类型。返回值常常是一个计算的结果,或者是用来作为判断函数执行状态(完成还是出错)的标记。
函数按是否有返回值来分类的话,分为:
1.无返回值的类型:针对函数无返回值或明确不需要返回的情况,使用void(即空类型)表示。
举例:输出函数void printf(const char*format,……)
2.有返回值的类型:指明具体的类型,比如:int float char等,如果省略,就默认为int类型
有返回值类型,则需要在函数体内与“return 返回值”搭配使用。返回值需要与返回类型一致
举例:int rand(),调用后返回一个随机整数
特殊的:如果返回值类型非void,但被调函数中没有return语句,函数会返回一个不确定的值。
情况1:函数在声明有返回值类型的情况下,如果没有在函数内部使用“return + 返回值”结构,则是错误的
比如:
#include "stdio.h"
int test() {
printf("hello\n");
//return 12;
}
int main() {
int i = test();
printf("%d",i);
return 0;
}
如果没有return 12main()函数就会输出一个不确定的数
函数名:
函数名,属于标识符,要遵循标识符的命名规则,同时要见名知意,以增强可读性
参数列表:
函数名后面的圆括号里面,可以声明参加的类型和参数名,表示完成函数体功能是需要外部提供的数据列表
根据是否有参数,函数可以分为:
无参函数,在调用无参函数时,主调函数不向被调用函数传递数据。但函数名后面的()不能省略
举例:abort(),立即终止程序的执行,不接受任何形参
有参函数,在调用函数时,主调函数在调用被调用函数时,通过形参向被调用函数传递数据
函数参数为多个参数时,其间用逗号隔开
举例:add(int m,int n) strcmp(const char*str1,const char*str2)
举例:
//打印5行6列的*型矩形
#include <stdio.h>
void print() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 6; j++) {
printf("*");
}
printf("\n");
}
}
//打印m行n列的*型矩形
void printfGraph(int m ,int n) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
printf("*");
}
printf("\n");
}
}
//如果确定括号中要打印的东西,就不用定义形参,如果打印的是不确定的,就要定义形参
int main() {
print();
printf("\n");
printfGraph(5,6);
return 0;
}
函数体:
函数体要写在大括号{}里面,是函数被调用后要执行的代码
对于调用者来说,不了解函数体如何实现的,并不影响函数的使用
关于return语句:
return语句的作用:1.结束函数的执行 2.将函数运算的结果返回
return 语句后面就不能再写其他代码了,否则会报错(和break,continue情况类似)
下面分两种情况讨论:
情况1:
返回值类型不是void时,函数体中必须保证一定有return +返回值;语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容
情况2:
返回值类型是void时,函数体中可以没有return语句,如果要用return语句提交结束函数的执行,那么return后面不能跟返回值,直接写return就可以
举例:
//return 的使用
/*return的作用是:
*1.结束函数的执行
*2.在有返回值类型的函数中,return +返回值这个结构还可以返回一个结果给函数
*return后就不可以在写其他的代码
*/
#include <stdio.h>
void show(int age) {
if (age > 22) {
printf("你可以结婚\n");
}
else{printf("你还不能结婚\n");}
}
void show1(int age) {
if (age > 22) {
printf("你可以结婚\n");
return;
//如果不加return输出的就不是预期效果。加了return之后,如果执行if语句可以直接提前结束该函数,可以达到预期效果
}
printf("你不能结婚");
}
int main() {
int age = 25;
show(age);
show1(age);
return 0;
}
函数声明的注意事项:
1.C语言程序中,所有的函数都是相互独立的,一个函数并不从属于另一个函数。即函数不能嵌套定义
2.同一个程序中不能重名,函数名用来唯一标识一个函数,即在标准C语言中,并不支持函数重载。
举例:
//函数声明的注意事项
#include <stdio.h>
//情况1:函数不能嵌套定义
/*比如*/void func1(){
//错误的
void func2(){
}
}
//情况2:同一个程序中函数不能重名,函数名是用来唯一标识一个函数,即在C语言中,是不支持函数重载的
//比如
int add(int i,int j) {
return i + j;
}
//下面这个函数是不能存在的,因为已经有一个名字是add的函数了
// int add(int i,int j,int k) {
// return i + j + k;
//
//}
//两种解决方案
//方案1,名字后面加数字
int add1(int i,int j) {
return i + j;
}
//方案2:换名字
int triAdd(int i,int j) {
return i + j;
}
函数的调用:
函数声明好后,直接引用函数名就可以调用
说明:
1.调用时,参数个数必须与函数声明里的参数个数一致,参数过多或过少都会报错
2.函数间可以相互调用,但不能调用main()函数,因为main()函数是被系统调用的,作为程序启动的入口。但是main()函数可以调用其他函数
3.函数的参数和返回类型,会根据需要自动进行类型转换
举例:
//函数的调用
#include <stdio.h>
void hello1() {
printf("hello1\n");
}
void hello2() {
printf("hello2\n");
}
void hello3(int num) {
printf("hello%d\n",num);
}
int main() {
hello1();
hello2();
hello3(3);
return 0;
}
//main()函数中可以调用其他函数,非main()函数中是不能调用main()函数的
//非main()函数之间可以相互调用
//将实参传递给函数的形参,要求实参的类型与形参的类型一致即可,既可以自动转换就可以
举例:
//定义求和函数getSum,求1+2+3……+n
#include "stdio.h"
int getSum() {
double total = 0;
int n;
printf("输入数字:");
scanf("%d",&n);
for (int i = 1; i <= n; i++) {
total += i;
}
return total;
}
int main() {
printf("%d",getSum());
return 0;
}
练习:哥德巴赫猜想
//任一大于2的偶数都可写成两个素数之和,利用判断素数的函数prime()验证哥德巴赫猜想
//素数:只能被1和自身整除的数,就意味着除了1和自身之外再无其他约数,在说明白一点就是从2到n-1之外没有约数
#include <math.h>
#include <stdio.h>
//问题1。如何判断一个数是素数
//如果函数返回1表示是素数,如果返回0表示就不是素数
int prime(int num) {
for (int i =2;i <= sqrt(num); i++) {//sqrt表示开根号
if (num % i == 0) {
return 0;
}
}return 1;
}
//问题2.任意的一个偶数是否可以写成两个素数之和
int main() {
int num;
printf("输入一个偶数:");
scanf("%d", &num);
for (int i = 2;i <= num / 2;i++) {
if (prime(i) && prime(num - i)) {
printf("%d = %d + %d",num ,i, num - i);
break;
}
}
}
练习:
//编一个函数char *getWeekName(int week),该函数接接收一个代表星期的数字(1代表星期1,7代表星期7),并返回相应的星期名称,如果输入无效,则返回空字符
#include <stdio.h>
// void*getWeekName(int week) {
// switch (week) {
// case 1:printf("星期一");//或者直接return Monday;下面的依次
// break;
// case 2:printf("星期二");
// break;
// case 3:printf("星期三");
// break;
// case 4:printf("星期四");
// break;
// case 5:printf("星期五");
// break;
// case 6:printf("星期六");
// break;
// case 7:printf("星期日");
// }
// }
// int i;
// int main() {
// printf("请输入数字:");
// scanf("%d",&i);
// printf("%s",getWeekName(i));
//
// return 0;
// }
//编写函数isLeapYear,检验输入的年份是否是闰年
//返回值:如果是闰年,返回1,否则返回0,如果输出为负数,则返回-1则为错误标准
//能被4整除但不能被100整除,或者能被400整除的就是闰年
// #include <stdio.h>
// int isLeapYear(int year) {
// if (year >0) {
// if (year % 4 == 0 && year %100 != 0) {
// return 1;
// }else if (year % 400 == 0) {
// return 1;
// }
// else {
// return 0;
// }
// }else{
// return -1;
// }
// }
// int year;
// int main() {
// printf("请输入数字:");
// scanf("%d",&year);
// printf("%d",isLeapYear(year));
//
//
// return 0;
//}
进一步认识函数:
关于main()函数:
main()的作用:
main()函数是程序的入口,所有程序一定要包含一个main()函数,程序总是从main()函数开始的,如果没有main函数,程序就无法启动
main函数可以调用其他函数,其他函数不能调用main函数,main函数也不能调用main函数
格式:
int main(){
return 0;
}
C语言约定,返回值0表示函数运行成功,返回其他非零整数值,表示运行失败。系统会根据main()返回值,确定程序状态
如果在main函数中省略return 0;,编译器会自动加上,但C语言只会对main()函数默认添加返回值,对其他函数不会这样,所以要养成加return 0;的习惯
main函数的其他写法:
main函数的声明中可以带有两个参数,格式如下:
int main(int argc,char *argv[]){
函数体
}
其中,形参argc,表示产给程序的参数个数,其值至少是1;而argv[]则是指向字符串的指针数组。
这种方式可以通过命令行的方式,接受指定的字符串传给参数argv。
太复杂了,用的还少,了解一下算了
关于exit()函数:
exit()函数用来终止整个程序的运行。一旦执行到该程序,程序就会立刻结束,该函数的原型定义在头文
件stdlib.h里面
exit可以向程序外部返回一个值,它的参数就是程序的返回值。一般来说,使用两个常量作为他的参数,这两个常量也定义在stdlib.h里面
EXIT_SUCCESS(相当于0),表示函数运行成功,正常结束;
EXIT_FALURE(相当于1),表示程序异常中止。
//程序运行成功
//等同于exit(0);
exit(EXIT_SUCCESS);
//程序运行失败
//等同于exit(1)
exit(EXIT_FAILURE);
在main函数中,会隐式的使用exit()函数,exit()函数等同于使用return语句。其他函数中使用exit函数,就是终止整个程序的运行,没有别的作用。
C语言中还提供了一个atexit()函数,用来登记exit()执行时额外执行的函数,用来做一些退出程序时的收尾工作,该函数的原型也是定义在头文件stdlib.h中
格式:
int atexit(void(*func)(void));
atexit()的参数是一个函数指针,注意,它的参数函数(下例的print)不能接受参数,也不能有返回值
也做了解。
函数原型:
函数必须先声明,后使用,所以其他函数都必须在main()函数之前
比如:
void func1() {
//
}
void func2() {
//
}
void func3() {
//
}
int main() {
func1();
func2();
func3();
return 0;
}
对于函数名较多的程序,保证所有函数的顺序正确会很麻烦,所以要用到函数原型,就是在程序开头处给出函数原型,函数就可以先使用后声明。
比如:
void func1;
void func2;
void func3();
int main() {
func1();
func2();
func3();
return 0;
}
void func1() {
//
}
void func2() {
//
}
void func3() {
//
}
所谓函数原型,就是函数在调用前提前告诉编译器每个函数的基本信息(返回值类型,函数名,参数个数,参数类型,参数顺序),其他信息都不需要。
在C语言中,通常在main函数之前或者是程序源码文件的开头,给出当前脚本使用的所有函数的原型,以确保不会报错
参数传递机制:
形参,实参:
形参:在定义函数时,函数名后面括号中声明的变量为形式参数,简称形参
实参:调用时,函数名后面括号中的值为实际参数,简称实参
形参只是一个形式,在调用之前并不分配内存。函数调用时,系统为形参分配内存单元,然后将主调函数中的实参传递给被调用的形参。被调用函数执行完毕,通过return语句返回结果,系统将形参的内存单元释放
形参和实参的功能主要是数据传递,按照传递的是“数据”还是地址,分为值传递和地址传递
参数传递机制1:值传递
值传递,又称传值方式,数据复制方式,就是把主调函数的实参值复制给调用函数的形参,使形参获得初始值,接着在函数内对形参值的修改,不影响实参值
值传递,是单向传递,只能把实参值传递给形参,不影响函数外部实参值
举例:
#include <stdio.h>
void increment(int a) {
a++;
printf("a = %d\n",a);// i = 11
}
int main() {
int i = 10;
printf("i = %d\n",i);//i = 10
increment(i);
printf("i = %d\n",i);//i = 10
return 0;
}
举例:
#include "stdio.h"
void swap(int a,int b) {
int temp = a;
a = b;
b = temp;
printf("a = %d,b = %d\n",a,b);
}
int main() {
int x = 6,y =8;
printf("调用函数之前:\n");
printf("x = %d,y = %d\n",x,y);
swap(x,y);
printf("调用函数之后:");
printf("x = %d,y = %d\n",x,y);
}
在这个例子中,在swap()函数内部变量a和b的值互换了,而主函数main()中实参x和y并没有交换,这是参数按值传递的原因
如图:

默认传递值的数据类型:
基本数据类型:整型,浮点型,字符型,结构体,共用体,枚举类型
参数传递机制2:地址传递
地址传递,又称传地址方式,地址复制方式,指针传递,就是把实参地址常量进行复制,传送给形参
默认传递地址的类型:指针,数组。实参将地址传递给形参,二者地址相同。
比如1:当指针作为函数的形参时,实参传递给形参的是地址,在函数中通过形参保存的地址访问实参,进而在函数中通过地址对实参的修改影响到实参的值,这也称为双向传递。
比如2:当传递数组首元素地址时,即把实参数组的起始地址传递给形参,这样形参和实参数组就占用了共同的存储空间,在被调函数中,如果通过形参修改数组元素时,调用函数后实参数组元素值也发生了变化
举例:
#include <stdio.h>
void swap(int *p1,int *p2);
int main() {
int x =10,y = 20;
swap(&x,&y);
printf("交换后的数字是:\n");
printf("%d %d",x,y);
return 0;
}
void swap(int *p1,int *p2) {
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
//输出为20 10
这才是正确的数值交换方法
特别提示:
int *f() {
int i//
//
//
return &i;
}
函数不要返回内部变量的指针。函数返回内部变量i的指针,这种写法是错误的,因为当函数结束运行后,内部变量就失效了,这时指向内部的变量i的内存地址就是无效的,再使用这个地址就是错误的
数组作为形参:
通过函数给数组元素赋值:
举例
//定义一个数组,通过函数给数组元素赋值
#include <stdio.h>
void setVALUE(int arr[],int num);
int main() {
int arr[10] = {0};
for (int i = 0; i < 10; i++) {
printf("%d ",arr[i]);
}printf("\n");
setVALUE(arr,10);
for (int i = 0; i < 10; i++) {
printf("%d ",arr[i]);
}
return 0;
}
void setVALUE(int arr[], int num) {
for (int i = 0; i < num; i++) {
arr[i] = i * 10;
}
}
字符串(字符指针)作为形参:
字符串(或字符指针)作为函数的参数,与数组指针作为函数参数没有本质区别,传递的都是地址值,所不同的仅是指针指向对象的类型不同。
举例1:定义函数,要求字符串做函数参数,统计数字字符出现的个数
#include <stdio.h>
#define N 100
int digitalCount(char *p) {
int count = 0;
for (;* p != '0';p++) {
if (*p > '0' && *p <= '9') {
count++;
}
}
return count;
}
int main() {
char strs[N] = "a12bc43hec22b68o";
printf("数字字符的个数为:%d", digitalCount(strs));
return 0;
}
指针数组作为形参:
指针数组的元素是指针变量,用指针数组能够实现一组字符串的处理
举例:
#include <stdio.h>
#include <string.h>
void stringSort(char *[],int);
void stringPrint(char *[],int);
int main() {
char *days[7] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
stringSort(days,7);
stringPrint(days,7);
return 0;
}
void stringSort(char *string[],int n) {
char *temp;
for (int i = 0;i < n;i++) {
for (int j = 0;j < n-1 -i;j++) {
if (strcmp(string[j],string[j+1]) > 0) {
temp = string[j];
string[j] = string[j+1];
string[j+1] = temp;
}
}
}
}
void stringPrint(char *string[],int n) {
for (int i = 0;i < n;i++) {
printf("%s\n",string[i]);
}
}
举例练习:
//定义函数,求一维数组元素的最大值
//函数原型:int pMax(int *p,int n)
//功能:在长度为n,由p指向的一维数组中元素最大值
#include <stdio.h>
int pMax(int *p,int n) {
int max = *p;//把首元素的地址赋值给max
for (int i = 0;i < n;i++) {
if (max < *(p + i)) {
max = *(p + i);
}
}
return max;
}
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//int *p = arr;
pMax(p,10);
printf("%d",pMax(p,10));
return 0;
}
//有一个3*4的矩阵,求所有元素中最大的值
#include <stdio.h>
int max(int *p,int n,int m);
int main() {
int arr[3][4] = {23,34,2342,34,4535,34,4234,34,34,543,546,235};
max(&arr[0][0],3,4);
printf("%d",max(&arr[0][0],3,4));
return 0;
}
int max(int *p,int n,int m) {
int max = *p;
for (int i = 0; i < n; i++) {
for (int j = 0;j < m;j++) {
if (max < *(p + i*m +j)) {
max = *(p + i*m +j);
}
}
}
return max;
}
这个也可以不用指针,直接用数组也行
c++中的引用传递:
C语言中采用的是传入变量地址的方式来实现的,相对麻烦还容易出错。这里可以用c++的语法。
了解一下算了
函数的高级应用:
递归函数:
函数自己调用自己的现象就成为递归
分类:
直接调用,间接调用
说明:
递归函数中包含了一种隐式的循环
递归函数会重复执行某段代码,但这种重复执行无需循环控制
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似死循环,最终发生栈内存溢出
C语言支持递归调用
举例:
//计算1-n的和
// #include <stdio.h>
// int sum(int n) {
// int total = 0;
// for (int i =1; i <= n;i++) {
// total += i;
// }
// return total;
// }
// int main() {
// int n;
// printf("输入一个数字:");
// scanf("%d",&n);
// printf("%d",sum(n));
//
//
// return 0;
// }
//或者用递归
#include <stdio.h>
int sum(int n) {//n默认大于0
if (n ==1) {
return 1;
}else{
return n + sum(n - 1);
}
}
int main() {
int n;
printf("输入数字:");
scanf("%d",&n);
printf("%d",sum(n));
return 0;
}
举例:算阶乘
#include <stdio.h>
int factorial(int n) {
if (n == 1) {
return 1;
}else {
return n* factorial(n - 1);
}
}
int main() {
int n;
printf("输入数字;");
scanf("%d",&n);
printf("%d",factorial(n));
return 0;
}
举例:斐波那契数列
//斐波那契数列:1 1 2 3 5 8 13 21,相当于从第三个数开始,每个数等于前两个数之和
#include <stdio.h>
int fibonacci(int n) {
if (n ==1 || n == 2) {
return 1;
}else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
int main() {
int n;
printf("输入一个正整数:");
scanf("%d",&n);
printf("%d",fibonacci(n));
return 0;
}
练习:走台阶问题(和斐波那契差不多)
//走台阶问题
//假如有10级台阶,每次只能向上走1阶或者2阶,请问对于n阶台阶一共有多少种不同的走法?
//阶数:1 2 3 4 ……
//走法:1 2 3 5 ……
//发现后一个走法的数量等于前两个走法的和
//表达式为:func(n) = func(n - 1) + func(n -2)
int zoufa(int n) {
if (n == 1 || n ==2) {
return 1;
}else {
return zoufa(n -1) + zoufa(n - 2);
}
}
int main() {
int n;
printf("输入台阶数量:");
scanf("%d",&n);
printf("%d",zoufa(n));
return 0;
}
总结:
1.使用递归函数大大简化了算法的编写
2.递归函数会占用大量系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢得多,所以在使用递归时要谨慎
3.在要求高性能时,尽量避免使用递归函数,递归调用,既花时间又耗内存,考虑使用循环
了解性内容:可变参数
有些函数的参数数量是不确定的,此时可以使用C语言提供的可变参数
声明可变参数的时候,使用省略号……表示可变数量的参数。
最常见的例子:
#include<stdarg.h>
int printf(const char* format,...);
这里的...表示可以传递任意数量的参数,但是他们都需要与format字符串中的 格式化标志相匹配。
注意:...必须放在参数序列的最后,否则会报错
可变参数函数的使用:
1.为了使用可变参数,要引入<stdarg.h>头文件
2.在函数中,需要声明一个va_list类型的变量来存储可变参数,他必须在操作可变参数时,首先使用。
3.使用va_start函数来初始化va_list类型的变量,他接受两个参数,参数1是可变参数对象,参数2是原始函数里面,可变参数之气的那个参数,用来为可变参数定位
4.使用va_start函数来诸葛获取可变参数的值,每次调用后,内部指针就会指向下一个可变参数,它接受两个参数,参数1是可变参函数对象,参数2是当前可变参数的类型
5.使用va_end函数来结束可变参数的处理
好复杂,了解一下算了
指针函数:
C语言中允许函数的返回值是一个指针(地址),这样的函数称为指针函数。
指针函数的格式:
返回值类型 *函数名(形参列表){
函数体
}
函数体中的return命令必须返回一个地址
举例1:
#include <stdio.h>
#include <string.h>
char *getMAXLengthstr(char *str1,char *str2) {
if (strlen(str1) > strlen(str2)) {
return str1;
}else {
return str2;
}
}
int main() {
printf("输入字符串:");
char str1[40],str2[50];
scanf("%s%s",&str1,&str2);
printf("%s",getMAXLengthstr(str1,str2));
return 0;
}
举例2:
//情景2:如下的函数在使用时是不对的
// int *func() {
// int m =10;
// return &m;//变量是一个局部变量,随着函数运行结束就弹出栈,地址失效,此时的返回值就不确定了
// }
// int main() {
// int *n = func();
// printf("*n = %d",*n);
//
//
//
// return 0;
// }
//情景3:如何修改情景2的情况
// int *func() {
// static int m = 10;//加了一个关键字static,此时m的地址存储在静态数据区,不会随着函数的执行而结束,而是会随着程序的结束而结束
// return &m;
// }
// int main() {
// int *n = func();
// printf("*n = %d",*n);
// return 0;
// }
//情景4:如何修改情景2,使用malloc函数
int *func() {
//malloc():使用此函数分配的内存空间是存储在堆空间的
int *m =(int *) malloc(sizeof(int));
if (m != NULL) {//只要分配堆内存成功,就给此变量赋值
*m = 10;
}
}
int main() {
int *n = func();
printf("*n = %d",*n);
return 0;
}
函数指针(指向函数的指针):
一个函数本身就是一段内存里面的代码,也会占用一段连续的空间,这段空间也有首地址,把函数的这个首地址(或称入口地址)赋予一个指针变量,是指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用这个函数,这种指针就是函数指针。
格式:
返回值类型 (*指针变量名)(参数列表);
其中,参数列表中可以同时给出参数的类型和名称;也可以只给出参数的类型,省略参数的名称
举例1:
#include <stdio.h>
void print(int m) {
printf("%d\n",m);
}
int main() {
//声明一个函数指针
void (*print_pointer)(int m);
//赋值操作
print_pointer = &print;
//函数的调用
//方式1:
print(10);
//方式2:使用函数指针
(*print_pointer)(10);
return 0;
}
举例2:
//测试函数指针的使用
//函数指针:即为指向函数的指针
#include <stdio.h>
int max(int a, int b) {
return (a > b)? a : b;
}
int main() {
//先定义一个函数指针
int(*pointer1)(int,int);
//赋值
pointer1 = &max;//max也行
//调用函数
//方法1
printf("%d\n",max(10,20));
//方式2
printf("%d",(*pointer1)(20,30));
return 0;
}
拓展:
C语言规定,函数名本身就是指向函数代码的指针,通过函数名就能获取函数的地址。也就是说,print和&print是一回事。上述例子中的赋值操作中的&就可以去掉。
注意:
1.对指向函数的指针变量不能进行算术运算,如p+n,p++,p--等运算都是无意义的
2.用函数名调用函数,只能调用所指定的一个函数,而通过指针变量调用函数就比较灵活,可以根据不同情况先后调用不同的函数(可以用在赋值操作这一步,以上述例子为例,可以在赋值时,可以把别的函数的地址赋值给pointer)
回调函数:
指向函数a的指针变量的一个重要用途是把函数a的入口地址作为参数传递到其他函数b中,此时的函数b就称为回调函数。
举例1:
#include<stdio.h>
#include<stdlib.h>
//回调函数
void initArray(int *array,int arrayLen,int (*f)()) {
for (int i = 0;i < arrayLen; i++) {
array[i] = (*f)();
}
}
int getRandom() {
return rand();
}
int main() {
int arrLen = 10;
int myarray[arrLen];
initArray(myarray,arrLen,getRandom);
//遍历数据
for (int i = 0;i < 10; i++) {
printf("%d\n",myarray[i]);
}
return 0;
}
举例2:
//有两个整数a和b,由用户输入1,2,3.如输入1,程序就给出a和b中的大者,如输出2,则给出a和b的小者,如输出3,则求a和b之和
// #include<stdio.h>
// int max(int a,int b) {
// return (a>b)?a:b;
// }
// int min(int a,int b) {
// return (a<b)?a:b;
// }
// int add(int a,int b) {
// return a+b;
// }
// int main() {
// int i,a,b;
// printf("请输入数字:");
// scanf("%d%d%d",&i,&a,&b);
// if (1<=i && i<=3) {
// switch (i) {
// case 1:
// printf( "%d",max(a,b));
// break;
// case 2:
// printf("%d",min(a,b));
// break;
// case 3:
// printf("%d",add(a,b));
// break;
// };
// }else(printf("请检查输入数字!"));
// return 0;
// }
//使用回调函数
#include <stdio.h>
int fun(int x,int y,int(*p)(int,int)) {
int result = (*p)(x,y);
return result;
}
int max(int x,int y) {
return(x > y)?x:y;
}
int min(int a,int b) {
return (a<b)?a:b;
}
int add(int a,int b) {
return a+b;
}
int main() {
int a = 10,b = 20;
int m;
printf("请输入数字:");
scanf("%d",&m);
switch (m) {
case 1:
printf("%d",fun(a,b,max));
break;
case 2:
printf("%d",fun(a,b,min));
break;
case 3:
printf("%d",fun(a,b,add));
break;
}
return 0;
}
函数的说明符:
函数一旦定义,就能被调用,但一个源程序由多个源文件组成时,在一个源文件中定义的函数能否被其他源文件函数调用呢?所以,函数分为了内部函数和外部函数。
内部函数(静态函数):
一个函数只能在内部使用的叫内部函数。
此时,内部函数需要用static修饰
格式:
static 类型说明符 函数名(<形参表>)
举例:
static int f(int a,int b) {
………………
}
外部函数:
外部函数在整个源程序中都有效。
在定义时,在函数前面加上extern关键字
因为函数在定义时是并列的,不能嵌套,所以函数在本质上都有外部的性质,因此在定义函数时省去extern说明符时,则隐含为外部函数
1025

被折叠的 条评论
为什么被折叠?



