• C语言- 指针与函数


    指针与函数

    • 指针作为函数的参数

      • 1,效果:在函数的内部可以通过这个参数指针去修改实参变量的值
      • 2,当函数的返回值有多个的时候
      • 3,地址传递
      • 4,如果函数的参数是一个指针,函数希望传递给调用者一个信息,函数内部只会去取指针指向变量的值,并不能修改指针指向变量的值
    • 指针作为函数的返回值

      • 1,指针当然可以作为函数的返回值
      • 2,但是不能返回局部变量的地址,就是返回的指针指向的变量一定要保证在函数结束之后,内个空间没有被回收,还是依然存在的
      • 如果你就是要返回一个指针,那么你就要保证这个指针指向的空间在函数结束以后仍然存在,那么这个时候就可以把空间申请在堆区,返回返回堆区的地址(堆区的地址只有 free 和程序结束的时候才会释放掉)
      int* test();
      
      int main(){
              // 指针作为函数的返回值,一定要返回一个局部变量依然还存在的地址
          int *arrNum = test();
          for (int i = 0; i < 4; i++) {
              printf("arrNum[%i] = %i
      ",i,arrNum[i]);
          }
          return 0;
      }
      
      int* test(){
          int arr[] = {1,2,3,45}; // 这是一个局部变量的数组,在程序运行完这个 test 函数后,该函数的内存地址就销毁了,虽然将数组 arr 的第一个字节地址返回了,但是此时已经不知道该地址指向了哪个值了
          
          return arr; // 将局部变量 arr 数组的地址返回,使用j指针接收
      }
      
      // 报错:Address of stack memory associated with local variable 'arr' returned返回的与本地变量“arr”相关联的堆栈内存地址
      
    • 解决办法:既然要返回一个还存在的内存地址,那么就将局部变量申请在堆空间中.申请在堆中的内存地址只有在 free()时和程序结束的时候才会回收

    int* Test();
    int main(){
        int *arrNum = Test();
        for (int i = 0; i < 3; i++) {
            printf("arrNum[%i] = %i
    ",i,arrNum[i]);
        }
        
        free(arrNum); // 释放掉堆中申请的内存
        return 0 ;
    }
    
    // 返回的指针为堆空间中的指针
    int* Test(){
        int* arr = calloc(3, sizeof(int));
        arr[0] = 10;
        arr[1] = 20;
        arr[2] = 30;
        return arr;
    }
    
     // 控制台输出:
    arrNum[0] = 10
    arrNum[1] = 20
    arrNum[2] = 30
    
    • 注意:
      • 返回值可以返回局部变量的值,但是不能返回局部变量的地址
      • 如果你非要返回指针,那么就应该把这个空间申请在堆区
      • 申请在常量区的空间,不会被回收的, 知道程序结束的时候才会回收
      • 以字符指针存储在常量区的字符串数据不能改,其他存储在常量区的数据是可以改的

    练习

    char* getWeek(int num);
    char* getWeekTwo(int num);
    char* getWeekThree(int num);
    
    int main(){
            // 写一个函数,传入 1-7 之间的数字,返回对应的星期几
        char* week = getWeek(8);
        char* weekTwo = getWeekTwo(4);
        char* weekThree = getWeekThree(5);
        printf("今天是:%s
    ",week);
        printf("今天是:%s
    ",weekTwo);
        printf("今天是:%s
    ",weekThree);
        
        return 0;
    }
    
    char* getWeekTwo(int num){
        switch (num) {
            case 1:
                return "星期一"; // 因为这个字符串就是存储在常量区的,返回常量区的地址,函数执行完常量区也还存在,并不是消失了
                break;
            case 2:
                return "星期二";
                break;
            case 3:
                return "星期三";
                break;
            case 4:
                return "星期四";
                break;
            case 5:
                return "星期五";
                break;
            case 6:
                return "星期六";
                break;
            case 7:
                return "星期日";
                break;
            
            default:
                return "输入错误";
                break;
        }
    };
    
    char* getWeekThree(int num){
        char* week = "未知";
        switch (num) {
            case 1:
                week = "星期一"; // 因为这个字符串就是存储在常量区的,返回常量区的地址,函数执行完常量区也还存在,并不是消失了
                break;
            case 2:
                week = "星期二";
                break;
            case 3:
                week = "星期三";
                break;
            case 4:
                week = "星期四";
                break;
            case 5:
                week = "星期五";
                break;
            case 6:
                week =  "星期六";
                break;
            case 7:
                week =  "星期日";
                break;
            
            default:
                week = "输入错误";
                break;
        }
        return week;
    };
    
    
    char* getWeek(int num){ // 最好不要用这种,返回字符串的时候
        char* week = calloc(12, sizeof(char)); // 从堆内存申请空间,但是一定要记住要释放对空间
        switch (num) {
            case 1:
                return week = "星期一";
                break;
            case 2:
                return week = "星期二";
                break;
            case 3:
                return week = "星期三";
                break;
            case 4:
                return week = "星期四";
                break;
            case 5:
                return week = "星期五";
                break;
            case 6:
                return week = "星期六";
                break;
            case 7:
                return week = "星期日";
                break;
            
            default:
                return week = "输入错误";
                break;
        }
    };
    

    指向函数的指针

    • 一般情况下都是指向一个变量,指针是指向内存中的一个字节空间
    int num = 10;
    int *p1 = &num;
    
    • 程序在运行的时候,会将程序加载到内存,程序中主要有代码/指令

    • 代码段中主要存储程序的代码,而程序的代码就包括函数,所以函数肯定要存储在内存的代码中,既然函数要存储在内存中,暗恶魔肯定要用一块空间,那么这个空间肯定有地址,那么我们就可以声明一个指针存储这个函数的地址,让这个指针间接的调用这个函数

    • 使用指针来间接调用函数

      • 优势:函数的调用有了两种方式
        • 直接使用函数名调用
        • 使用指向函数的指针进行调用
    • 指向函数的指针声明

      • 一个贺子珍函数的指针,并不是任一的函数都可以指向,而是有限定的,要求指向的函数的返回值类型与参数描述必须要与指针的描述一样

      • 声明语法

        • 返回值类型(*指针名)([参数列表]);
        • void (*pFunction)();
        • 表示声明了一个指向函数的指针,名字叫做 pFunction,这个指针只能之乡没有返回值,并且没有参数的函数
        • int (*pFunction)(int num1 ,int num2);
        • 表示声明了一个返回值为 int 类型,函数名是 pFunction,参数有两个 int 类型的参数的函数指针
    • 指向函数的指针初始化

      • 1,取到符合指针条件的函数地址
        • 函数的名称就是代表函数的地址
      • 2,将地址赋值给指针变量
        • 指针将符合条件的函数名称赋值给这个指针
      • 3,注意
        • 函数名代表函数的地址
        • 不要函数名加小括号,如果加了就代表执行这个函数,拿到这个函数的返回值
    • 如何使用指针调用指向的函数呢

      • 第一种:p1();
      • 第二种:(*p1)(); 有参数就给,又返回值就接
    • 小技巧:如果要定义一个指针指向 1 个函数,拷贝这个函数头,删除函数名,用小括号代替,里面写上*加指针名称

    void defFun();
    int sum(int num1 ,int num2);
    int main(){
        // 指针指向函数
        void (*p1)() = defFun;
        p1();
        (*p1)();
        
        int (*qiuhe)(int num1, int num2) = sum; // 声明一个指针叫做 qiuhe,指向一个int 类型的返回值,有两个参数,两个参数的类型都是 int 类型的函数,将 sum 函数的地址赋值给这个指针,进行初始化
        qiuhe(1,2);
        (*qiuhe)(3,4);
        return 0;
    }
    void defFun(){
        printf("这是一个被指针指向的函数
    ");
    }
    
    int sum(int num1,int num2){
        int sum = num1+num2;
        printf("sum = %i
    ",sum);
        return num1+num2;
    }
    
  • 相关阅读:
    C# DataGridView 与 datatable 之间数据传递
    C# 调用命令行命令 net use
    C# 链接 sql server 数据库字符串
    winform窗口关闭,进程没有关掉的解决办法
    select 中的逻辑判断 sql server
    C#中的abstract 类和方法!!!
    c# ComboBox绑定枚举
    C#与C++类型互转
    DllImport
    TCP三次握手四次挥手详解
  • 原文地址:https://www.cnblogs.com/shanshan-test/p/13121250.html
Copyright © 2020-2023  润新知