C语言基础8函数高级用法

目录

一、指针函数

1.基本形式

2.编程实例

(1)例:编写一个指针函数, 删除一个字符串中的空格

(2)例:编写一个指针函数, 实现字符串连接

(3)例:编写一个指针函数,把整数123转换成字符串“123”。

3.总结

二、递归函数

1.基本概念

2.编程实例

(1)编写一个递归函数,计算n!编写一个递归函数

(2)计算斐波那契数列

三、函数指针

1.表达形式

用在什么地方?

2.函数指针数组

3.总结


一、指针函数

1.基本形式

指针函数是指一个函数的返回值为地址量的函数
指针函数的定义的一般形式如下
<数据类型>  *  <函数名称>(<参数说明>)
 {      语句序列;}

*返回值:全局变量的地址/static变量的地址/字符串常量的地址/堆的地址(需要学maloc).

下面的代码是否有问题,若有问题,如何修改?

#include <stdio.h>

char * mystring(){
        char str[20];
        strcpy(str, "Hello");
        return str; 
}

int main(int argc, const char * argv[])
{
        printf("%s\n",mystring());        //error
        return 0;
}

mystring返回的是一个指针。因为str是个局部变量。用的是栈上空间,自动分配自动回收。

改正方案:

1全局变量,但是没必要再去用返回值,直接出结果了。
2用静态变量static,变量的寿命得到了延长 static char str[20];
3字符串常量 char * str = "hello"; 但是不方便修改

#include <stdio.h>
#include <string.h>
char * mystring(){
        static char str[20];
        strcpy(str, "Hello");
        return str; 
}

int main(int argc, const char * argv[])
{
        printf("%s\n",mystring());       //ok
        return 0;
}

2.编程实例

(1)例:编写一个指针函数, 删除一个字符串中的空格

#include <stdio.h>
#include <string.h>

char *Del_Space(char * s);

int main(int argc, const char * argv[])
{
        char str[] = "who are you?";
        char s[50], s2[25];
        strcpy(s2,strcpy(s,Del_Space(str)));
        printf("%s\n", s);
        return 0;
}

char *Del_Space(char * s){

        char * r =s;     //指向头指针
        char * p =s;
        while(*s ){            //等同于 while(*s != '\0')
                if(*s == ' '){
                        s++;
                }
                else{
                        *p = *s;
                        s++;
                        p++;
                }
        }
        *p = '\0';              //需要注意\0要写过来
        return r;
}
//结果

whoareyou?                                                                                                                                                                                                 

分解语句s2是目标指针,strcpy(s,del_space(str))是源指针。在往里分解其中s是目标指针,del_space(str)是源指针去掉了空格。相当于连续赋值,但是指针里面用的是strcpy函数。这个函数其实返回的既不是全局变量,也不是局部变量,也不是静态变量,也不是堆空间,也不是字符串常量。其实返回的是一个参数

(2)例:编写一个指针函数, 实现字符串连接

#include <stdio.h>
#include <string.h>

char *Mstrcat(char * dest, char * src);

int main(int argc, const char * argv[])
{
        char str1[50] = "who are you?";
        char str2[] = "Your Dad";
        printf("%s\n",Mstrcat(str1 , str2));
        return 0;
}

char *Mstrcat(char * dest, char * src){

        char * r = dest;
        while( * dest){             //可以简化成 while(* dest++) 
                dest++;
        }
        while( * src){              //可以简化成 while(*dest++ = *src++)
                *dest = *src;
                dest++;
                src++;
        }

        *dest = '\0';              //需要注意\0要写过来
        return r;
}
//结果
who are you?Your Dad

设计也是考虑2个指针,目标指针找到\0位置,然后接新串,并各自增。再找个指针,记录原来的位置

(3)例:编写一个指针函数,把整数123转换成字符串“123”。

 应用场景,比如网络上面给一个6000端口号,把字符串变成整数,叫做atoi,还有itoa。

#include <stdio.h>

char * itoa(int n);

int main()
{
    int n; 
    char * s;

    printf("input:");
    scanf("%d", &n);

    s = itoa(n);

    puts(s);

    return 0;
}

char * itoa(int n)//itoa()自定义函数名称,整数转换成字符串
{
    int r, i, j;
    static char p[50];

    while(n){
        r = n % 10;
        n /= 10;
        p[i] = r + '0';//数字和字符的ASCII码相差48
        i++;
    }
    p[i] = '\0';

    j = i-1;
    i = 0;

    while(i < j){//数组反转,两个指针对位交换,从两边向中间走。
        r = p[i];
        p[i] = p[j];
        p[j] = r;
        i++;
        j--;
    }
    return p;
}

另一种写法 ,如果题目给了字符串空间,我们需要2个参数

#include <stdio.h>

char * itoa(char * p, int n);

int main()
{
    int n;
    char s[50], *r;//给一个数组,可以利用数组的空间
//  char * s;

    printf("input:");
    scanf("%d", &n);

    r = itoa(s,n);//把输入的(n)放到数组(s)中

    puts(r);
    puts(s);

    return 0;
}

char * itoa(char * p, int n)
{
    int r, i = 0, j;
//  static char p[50];数组不用声明空间了,数组空间是上面传递来的
//不用再数组内部考虑空间大小
    while(n){
        r = n % 10;
        n /= 10;
        p[i] = r + '0';
        i++;
    }
    p[i] = '\0';

    j = i-1;
    i = 0;

    while(i < j){
        r = p[i];
        p[i] = p[j];
        p[j] = r;
        i++;
        j--;
    }
    return p;
}

扩展把字符串转换成整数

#include <stdio.h>
#include <string.h>

int *atoi(char * arr);

int main(int argc, const char * argv[])
{
        char str1[] = "123";
        int * p;
        p = atoi(str1);
        printf("%d",(int)p);
        return 0;
}

int *atoi(char * arr){

        int  n = 0;
        while( * arr){
                n = n * 10+(*arr - '0') ;
                arr++;
        }
        return (int *)n;
}

//结果123

3.总结

主要介绍了指针函数的概念以及指针函数的编写

为什么要用指针函数?

答:主要是能够用一个指针的方式指向一个函数,并且还可以更换指向别的函数,比如有多个函数的申明,它们有不同的具体实现,如果需要调用它们,就可以用一个指针轮流指向它们。

指针函数可以返回什么样的指针?

函数可以返回整形、字符串、实型值、无返回值、也可以返回指针类型的数据,即返回一个内存地址,像这样返回地址的函数成为指针函数。

二、递归函数

1.基本概念

递归函数是指一个函数的函数体中直接或间接调用了该函数自身递
归函数调用的执行过程分为两个阶段: 

  • 递推阶段:从原问题出发,按递归公式递推从未知到已知最终达到递归终止条件
  • 回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解

2.编程实例

(1)编写一个递归函数,计算n!编写一个递归函数

思路:

f(10)  = 10!=10*9!        9!=9*8!        ...一直到        1!=1*0!        规定0的阶乘=1,递归回原路。

递归阶段:f(n) = n*f(n-1)

回归阶段:规定0! = 1,程序找到归口之后迭代回去(必须有归口,否则要死循环)

#include <stdio.h>
#include <string.h>

int recursion(int n);

int main(int argc, const char * argv[])
{
        int n;
        printf("input number:\n");
        scanf("%d",&n);
        printf("%d\n", recursion(n));
        return 0;
}

int recursion(int n){
        if(n == 0){
                return 1;
        }
        while(n>1){
                return n*recursion(n-1);
        }
}

//结果
syj@ubuntu:~$ ./a.out 
input number:
1
1
syj@ubuntu:~$ ./a.out 
input number:
0
1
syj@ubuntu:~$ ./a.out 
input number:
10

(2)计算斐波那契数列

一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?我们不妨拿新出生的一对小兔子分析一下:第一个月小兔子没有繁殖能力,所以还是一对;两个月后,生下一对小兔,对数共有两对;三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对。

思路:

数列:1、1、2、3、5、8、13、21、34...

递归阶段:f(n) = f(n-1) + f(n-2)

回归阶段:规定f(1) = 1 ; f(2) =1;把归口条件迭代回公示内交给程序计算

#include <stdio.h>
#include <string.h>

int fib(int n);

int main(int argc, const char * argv[])
{
    int n = 1;

    while (n <= 10){
        printf("%d ", fib(n));
        n++;
    }

    printf("\n");

    return 0;
}

int fib(int n){
        int i = 1;
        if(n == 1 || n == 2){
                return 1;
        }
        while(i <= n){
                return fib(n-1)+fib(n-2);
        }
}

//结果
1 1 2 3 5 8 13 21 34 55 

三、函数指针

1.表达形式

1函数指针用来存放函数的地址,这个地址是一个函数的入口地址
2函数名代表了函数的入口地址
3函数指针变量说明的一般形式如下
<数据类型> (*<函数指针名称>)(<参数说明列表>);

  • -<数据类型>是函数指针所指向的函数的返回值类型
  • -<参数说明列表>应该与函数指针所指向的函数的形参说明保持一致
  • -(*<函数指针名称>)中,*说明为指针()不可缺省,表明为函数的指针

调用就是(*p)(m,n)把函数名换成(*P)

用在什么地方?

使得函数功能更强,比如排序,能整型、字符、double都能排序

程序案例:

#include <stdio.h>

int add(int a, int b)
{
    return a+b;
}

int main()
{
    int m = 10, n = 20; 
  
		//声明时形参可以省略,写出函数原型即可
    int (* p)(int a, int b); //函数指针的声明
    p = add;//赋值

//  printf("%d\n", add(m,n));
    printf("%d\n", (* p)(m, n));//调用
    return 0;
}

//结果
//30

2.函数指针数组

函数指针数组是一个保存若干个函数名的数组
一般形式如下
<数据类型>  (*<函数指针数组名称> [<大小>] )(<参数说明列表> );
其中,<大小>是指函数指针数组元数的个数其它同普通的函数指针 类似指针数组

编程实例

#include <stdio.h>

int add(int a, int b)
{
    return a+b;
}

int sub(int a, int b)
{
    return a-b;
}

int main()
{
    int m = 10, n = 20; 

    int (* p[2])(int, int);

    p[0] = add;
    printf("%d\n", (* p[0])(m, n));

    p[1] = sub;
    printf("%d\n", (* p[1])(m, n));
    return 0;
}

//结果
30
-10

3.总结

主要介绍了递归函数,函数指针以及函数指针数组的相关内容思考编写递归函数要注意什么问题?

答:递归阶段正不正确;回归阶段结束条件是否正确


程序:调用C库中的qsort函数来实现整形数组的排序。

qsort函数数组原型

void qsort(void * base, size_t nmemb, size_t size, int ( * compar) (const void *, const void *));
通过linux的man qsort命令查看函数要求:

函数的作用是:对包含nmemb元素的数组进行排序。base参数指向数组的开始。

数组的内容根据compare所指向的比较函数升序排序,调用该函数时有两个参数指向被比较的对象。

如果第一个参数被认为分别小于、等于或者大于第二个,比较函数必须返回一个小于、等于或大于零的整数。如果两个成员比较相等,则它们在排序数组中的顺序未定义。

qsort_r()函数与qsort()相同,不同之处是比较函数compare有第三个参数。一个指针被传递给比较函数

通过参数。通过这种方式,比较函数不需要使用全局变量来传递任意参数,因此是可重入的,使用起来是安全的在线程。

#include <stdio.h>
#include <stdlib.h>

int compare(const void * p, const void *q);

int main()
{
    int s[] = {89, 58,78,10,8,7,64}, n, i;

    n = sizeof(s)/sizeof(int);

    qsort(s, n, sizeof(int), compare);

    for(i = 0; i < n; i++)
        printf("%d ", s[i]);
    puts("");

    return 0;
}

int compare(const void * p, const void *q) 
{//p、q为待比较的两个元素
    return (*(int *)p - *(int *)q);//如果是q-p,那么是降序      
  //由于函数参数为void型,所以要进行强制转换
}

//结果
7 8 10 58 64 78 89 //升序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言中,回调函数是非常常见的一种编程技术。回调函数是指一个由外部程序或库函数传递给另一个函数函数指针。当某个事件发生时,另一个函数会调用这个函数指针,以便执行特定的操作。 除了基本的回调函数用法,C语言中还有一些高级的回调函数用法,下面介绍一些常见的用法。 1. 函数指针数组 函数指针数组是指一个数组,该数组的每个元素都是一个指向函数的指针。函数指针数组可以用来实现一个分发器(dispatcher)函数,该函数根据传入的参数来选择要调用的函数。 示例代码: ```c #include <stdio.h> void foo1() { printf("foo1\n"); } void foo2() { printf("foo2\n"); } void foo3() { printf("foo3\n"); } int main() { void (*funcs[])() = { foo1, foo2, foo3 }; int i; for (i = 0; i < 3; i++) { funcs[i](); } return 0; } ``` 在上面的代码中,我们定义了三个函数foo1、foo2和foo3,然后定义了一个函数指针数组funcs,该数组的每个元素都是一个指向函数的指针。在主函数中,我们循环遍历该数组,并依次调用每个函数。 2. 回调函数结构体 回调函数结构体是指一个结构体,该结构体包含了一个函数指针和一些其他的数据。回调函数结构体可以用来传递多个参数给回调函数。 示例代码: ```c #include <stdio.h> typedef struct { void (*callback)(int); int data; } callback_struct_t; void callback_func(int data) { printf("callback_func: %d\n", data); } int main() { callback_struct_t cb = { callback_func, 123 }; cb.callback(cb.data); return 0; } ``` 在上面的代码中,我们定义了一个回调函数callback_func,该函数接受一个整数参数,并输出该参数的值。然后定义了一个回调函数结构体cb,该结构体包含了一个函数指针callback和一个整数数据data。在主函数中,我们将回调函数callback_func和整数数据123分别赋值给cb的callback和data成员,然后调用cb.callback(cb.data),即可调用回调函数。 3. 匿名函数 匿名函数是指没有名字的函数,也叫做lambda函数。在C语言中,可以使用函数指针和结构体来实现匿名函数。 示例代码: ```c #include <stdio.h> typedef struct { int (*calculate)(int, int); } calculator_t; int main() { calculator_t calc = { .calculate = ^(int a, int b) { return a + b; } }; printf("%d\n", calc.calculate(2, 3)); return 0; } ``` 在上面的代码中,我们定义了一个结构体calculator_t,该结构体包含了一个函数指针calculate。在主函数中,我们使用了一个匿名函数来实现calculate函数,该函数接受两个整数参数,并返回它们的和。然后将该匿名函数赋值给calc的calculate成员,并调用该函数,输出结果。 以上是C语言中一些常见的回调函数高级用法,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

4IOT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值