一、回调函数
可以通过函数指针的方式把一个函数注入到另一个函数里,这种注射的函数叫做回调函数,例如
#include<stdio.h>
int add(int a,int b){
return a+b;
}
int text(int a,int(*p)(int c,int d)){//将add函数注入到text函数中
return a+p(5,6);
}
int main(int argc,const char*argv[]){
int ret=text(5,add);//5+(5+6)
printf("%d\n",ret);//16
return 0;
}
二、函数间数据传递
1、值传递
单向传递,将实参传递给形参使用,改变形参时不会影响实参值
2、地址传递
双向传递,在函数中修改形参值,会影响实参
注:也就是说,假设我们写一个两个数交换位置的函数,如果用值传递的方式,那么主函数中的数值不会发生改变,但是用地址传递就可以改变其内容,例如
#include<stdio.h>
void change(int a,int b){//值传递
int t;
t=a;
a=b;
b=t;
}
void change1(int *a,int *b){//地址传递
int t;
t=*a;
*a=*b;
*b=t;
}
int main(void){
int x=5,y=10;
change(x,y);
printf("x=%d y=%d\n",x,y);//x=5,y=10,不会改变
change1(&x,&y);
printf("x=%d y=%d\n",x,y);//x=10,y=5,会被改变
return 0
}
3、数组传递
和地址传递一样,参数中存在数组定义,它会认为是指针,可以用来实现双向数据传递,例如
#include<stdio.h>
#include<stdlib.h>//rand()、srand()函数所需头文件
#include<time.h>//time函数所需头文件
void create(int arr[],int size){
int num=0;
for(num=0;num<=size-1;num++){
arr[num]=rand()%36+1;//取1~36的随机数
}
}
int main(void){
int arr[7]={0};
int num=0;
srand(time(0));//生成随机数
create(arr,7);
for(num=0;num<7;num++)
printf("%d ",arr[num]);//会打印出放到数组中的随机数
printf("\n");
return 0;
}
也可以将地址当做返回值传回给调用函数,例如:
4、无类型指针当作形参使用
可以让调用函数向被调用函数开放任意类型的存储区,例如:
#include<stdio.h>
void(const void *p_v,int type){
if(!type){
printf("%c\n",*(char*)p_v);//以char的类型访问
}
else if(type==1){
printf("%d\n",*(int*)p_v);//以int的类型访问
}
}
int main(void){
char ch='e';
int val=56;
print(&ch,0);
print(&val,1);
return 0;
}
这里涉及到强制转换,(数据类型*)指针或者是(数据类型)变量,进行强制转换,例如上述的void*我理解就是不去访问地址,(char*)强转后表示以char的方式去访问该地址;
比如float a=3.14;当我printf("%d",(int)a);将会输出3,将a强转成int类型了
5、调用函数的存储区地址可以做返回值传递
使用时注意生命周期,例如:
#include<stdio.h>
int *read(void){
static int val=0;//静态局部变量的声明周期符合要求,如果不加static,val存储区在程序结束
//后就会被释放,主函数无法找到存储区中的数值了
printf("请输入一个数:\n");
scanf("%d",&val);
return &val;
}
int main(void){
int *p_val=read();
printf("%d\n",*p_val);
return 0;
}
三、递归函数
c语言中函数自己可以调用自己,这样的函数就是递归函数
使用递归函数解决问题时,一般是一个复杂的问题可以使用一种方法不停地进行分解,分解以后问题比之前更简单
编写步骤:
①编写语句解决分解之后的每个小问题;
②递归调用语句之前编写分支解决不可分解的情况以保证函数可以结束
例如用递归函数来显示费氏数列:
费氏数列: 1 1 2 3 5 8 13 21......
编号: 0 1 2 3 4 5 6 7......
注:加static是为了延长生命周期,这样递归函数每次调用自己的时候前面算过存在数组内的值就不需要重新计算,不加static时每次调用都会重新申请数组,因此当给出的数比较大时,计算会非常慢。
综合一下,写个带结构体的函数,并且运用动态分配;
函数1从键盘得到一个水平长方形位置,把它记录在动态分配内存里,最后把位置传递给调用函数
函数2从调用函数得到水平长方形位置,计算中心点并记录在动态分配的内存里在传递给调用函数
四、常用的一些函数与string族函数
头文件<string.h>
char *stpcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
功能:将一串字符串的内容拷贝到另一个数组里面,会覆盖原来的内容
参数:
dest:源字符数组地址
src:要拷贝进去的字符串
n:要拷贝进去的个数
返回值:返回拷贝完后的字符串地址
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
功能:把一串字符串的内容追加到另一个字符串末尾,注意数组空间要能装下合并后的字符串
参数:
dest:源字符串
src:要追加的字符串
n:代表要追加的个数(没有n就将src都追加进去)
返回值:返回一个指向结果字符串的地址
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较两个字符串的大小
参数:
s1、s2:两个字符串
n:限定比较前几个字符,
”hello“和”world“比较,先是h与w比较,w大,后面的就不用比较了
”hello“和”heall“比较,h相等e相等,l大于a,前面大,但如果n等于2,那么只比较前两个字符,则相等
返回值: 1 前面大
-1 后面大
0 一样大
char *strstr(const char *haystack, const char *needle);
功能:从一个字符串里找一个字符串的位置
参数:显而易见两个字符串
返回值:找到的第一个字符的位置,找不到返回NULL
例如char *p_ch=str("abcdef","cd"); printf("%s\n",p_ch);//显示cdef
void *memset(void *s, int c, size_t n);//把一组连续字符类型存储区的前n个设置成同一个字符参数c
void bzero(void *s, size_t n);//把一组连续字符类型存储去的前n个设置成0;
头文件<stdlib.h>
int atoi(const char *nptr);//把字符串开头整数部分转换成整数类型
double atof(const char *nptr);//把字符串开头部分浮点数转换为浮点数类型
#include<stdio.h>
#include<stdlib.h>
int main(void){
int val=0;
double dval=0.0;
val=atoi("35jsdh");
dval=atof("25.99fslg");
printf("val是%d\n,dval是%lg\n",val,dval);//结果是35和25.99
return 0;
}
头文件<stdio.h>
int sscanf(const char *str, const char *format, ...);//从字符串里获得数字记录到存储区
int sprintf(char *str, const char *format, ...);//数字转换成字符串记录到字符数组
#include<stdio.h>
#include<string.h>
int main(void){
char arr[20]={0};
int val=0;
float fval=0.0f;
char ch=0;
sprintf(arr,"%d%g%c\n",40,6.4f,'t');
printf("%s",arr);//406.4t
sscanf("a556.3","%c%d%g",&ch,&val,&fval);
printf("%g%d%c\n",fval,val,ch);//0.3 556 w
return 0;
}