小白学习笔记(函数)

函数的基本使用:

将特定代码封装成函数的好处:实现代码复用,减少冗余,简化代码

一个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说明符时,则隐含为外部函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值