C语言【学习篇】七

函数

  • 函数的定义:返回值数据类型 函数名([形参说明表])
    • 实参数:main中输入参数
    • 形参数:被调用函数中,函数调用完成则参数结束
  • 函数的传参:
    • 值传递:print_value(i, j);
    • 地址传递:swap_number(&i, &j);//在调用函数中实现对实参的改变
    • 全局变量
  • 函数的调用:
    • 嵌套调用:a–>b–>c
    • 递归调用:直接或间接的调用自己,递归是嵌套调用的特列
  • 一些小练习
//递归实现n的阶乘实现
int GetNmul(int n){
    int Nmul = 0;
    if(n < 0){
        return -1;//enter error
    }
    if(n == 0 || n == 1){
        return 1;
    }
    Nmul = GetNmul( n - 1 );
    return Nmul * n;
}
//递归实现fiboncci数列
int Fibonacci(int* arr, int size){
    if (size <= 0)
    {
        return 0;
    }
    if(size == 1){
        arr[size - 1] = 1;
        return 1;
    }
    if(size == 2){
        arr[size - 1] = 1;
        arr[size - 2] = 1;
        return 1;
    }
    arr[size - 1] = Fibonacci(arr, size - 1) + Fibonacci(arr, size - 2);
    return arr[size - 1];
}
  • 函数与数组:
    • 一维数组:
      • main函数传入调用数组—实参:数组名and长度—形参:接受数组名可以用int* arr 或者 int arr[]
      • 在形参中一个[]等价于一个*
        int a[N] = {1,2,3,4};
        int* p = a;
        实参->   a   *a   a[0]   &a[3]   p[i]   p   *p   p+1  
        形参-> int*  int  int    int*    int   int* int  int*
        //一维数组的地址就相当于一维指针
        
    • 二维数组:
      • 几种传参的解释:
      //主函数调用
      //int arr[ROW][LIST] = {1,2,3,4,5,6}; 
      //PrintNum1(*arr, ROW*LIST);--&arr[0][0] a[0] 
      void PrintNum1(int* arr, int size){
          int i = 0;
          for(i = 0; i < size; i++){
              printf("%d ", arr[i]);
          }
          printf("\n");
      }
      //主函数调用
      //int arr[ROW][LIST] = {1,2,3,4,5,6}; 
      //PrintNum2(arr, ROW, LIST);--arr本质上就是一个存放数组的指针即数组指针
      void PrintNum2(int (*p)[LIST], int row, int list){
        //int (*p)[LIST]--可替换-- int p[][LIST]
        int i = 0;
        int j = 0;
        for(; i < row; i++){
          for(j = 0; j < list; j++){
              printf("%4d", *(*(p + i) + j));
              //替换后调用p[i][j]
          }
          printf("\n");
        }
      }
      
      • 传参对应表
      int a[M][N] = {...};
      int *p = *a;
      int (*q)[N] = a;
      实参-> a[i][j]  *(a+i)+j  a[i]+j  p[i]  *p  q[i][j]  *q     q      p+3    q+2
      形参-> int      int*      int*    int   int  int    int* int(*)[]  int*   int(*)[]
      一组常用的逻辑:
      实参->  a-->行指针                  *a-->列指针  
      形参-> int (*p)[]                  int *p
      
      • 讨论一个问题:为什么二级指针不能指向二维数组的数组名?
        • 什么是二级指针?
          • 二级指针是指向一级指针的指针,一级指针是取一次地址,二级指针是取两次地址。这个没有异议吧。 这个可以扩展成:n级指针是指向n-1级指针的指针,n级指针是取n次地址。
        • 二维数组取几次地址可以读取?
          • 因为二维数组是连续存放的。取了一次地址之后,所有元素的地址就全部知道了,只需要指针的移动就可以遍历所有的元素了,不需要再取一次地址了。
        • 为什么不能呢?
          • int **p=a;//编译出错,不能用二级指针直接指向二维数组,我们不妨用替换的思维考虑这个问题,我们做出如下等价 int **p = a <–> &a[0] <–> &a[0][0] 这时候p显然只能为int *p,故而不成立。
        • 什么情况下使用二级指针呢?
          • char *str[2]={“hello”,“world”};char **pp;pp=str;//这种情况是可以的。
          • 本质原因是str[2]为一维数组,但是它的两个元素都是char* 型的,都是地址。所以,*pp=str[0],str[0]不是像上面的那样是个定值1,这里的str[0]是一个地址,是字符串“hello”的首地址,所以还要再取一次地址才能访问到字符串“hello”的每个字符。这就是需要两次取地址了。这时候用二级指针就没有问题了。二级指针不就是取两次地址嘛。
    • 函数与字符数组:
      char* mystrcopy(char* dest, const char* str){
          char* ret = dest;
          if(dest != NULL || str != NULL){
              while ((*dest++ = *str++) != '\0');
              //将str赋给dest且dest不等于'\0' 但是由于是先赋值因此刚好将'\0'赋入,
              //若不是则需要考虑在尾部加入'\0'
          }
          return ret;
      }
      
  • 函数与指针:
    • 指针函数:是一个函数返回值是指针
      • 返回值* 函数名(形参); int* name(int i)
    • 函数指针:一个指针指向函数
      • 类型 (*指针名) (形参)//此处去掉( *指针名)部分其实就是函数的类型; int ( *p)(int);
      • 理解:函数名其实是一段代码所关联的入口地址
        int add(int num1, int num2){
          return a+b;
        }
        //main:
          int (*p)(int,int) = add;
          printf("%d", p(3,4));
        
    • 函数指针数组:是一个数组保存函数指针
      • 类型 (*数组名[下标]) (形参); int ( *arr[N]) (int);
      • 解释:arr是一个数组大小为N,N个元素都是指向函数的指针
    • 指向指针函数的函数指针数组:
      • 分析:
        • 一个数组arr[N]
        • 存放函数指针即为指向函数的指针 int *arrN
        • 指针函数 int* (*arr[N])(int)
    • 既然函数的函数名称就是入口地址,为什么还需要一个函数指针?— 方便传参
      • eg: void qsort(void* base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));

你可能感兴趣的:(c语言,学习)