一、函数


1. 函数的思想
2. 函数语法 
   函数定义 
      //位置
      main函数之前 
      main函数之后 
   函数调用 
      main函数之前定义 直接用 
      main函数之后定义 函数声明 //函数头 复制 后 加个分号 
                             //必须放到 使用之前 
3. 函数定义的细节
   //函数返回值类型 
   //函数名
   //形参 
     //形参和实参 ---- 个数 类型 顺序
   //函数体 
   
4. 设计函数原则 
   功能单一原则 
   是否要反复被用 
  
数据梳理流程 (数据流)
实参给到形参 ---实际就是把实参的值 给到形参变量中 ---- 值传递 
函数与函数之间的数据传递 ---- //全局变量 

为了遵循高内聚、低耦合的设计原则,应尽量避免使用全局变量。全局状态会破坏模块独立性,增加代码维护成本。

5. 代码出问题 或 没达到效果 
   怎么办?
   调试代码  
   //最基本的方法 ---就是加打印 
   // 把程序运行过程中的关键数据打印出来 


函数递归


   递归 --- 自己调用自己 
   
   递归 --- 1. 是一种特殊的循环 
           2. 如果不加结束条件,最终也会结束 ----函数调用-- 栈空间耗尽 
           3. for/while/do-while
              for (;x;) //死循环
              while(1)
              {
                 
              }
              do-while(1)
   
  例子:1+2+3+ ...+100 //累加求和 
  
  sum = sum + i;
  
  //递归的思想 
  
  sum(100) 
     |---sum(99) + 100
           |---sum(98)+99
                |---sum(97)+98
                      |---sum(96)+97
                          .... 
                              sum(3)+4
                                 |---sum(2)+3
                                       |---sum(1)+ 2
                                           //1  

         
   
 递归解决问题的思路:
    1.要解决问题n,
      要看问题n-1的解决 
    2.当一个函数需要被自身重复调用时---可以考虑递归 


//递归实现编程: 两个要素 
  1.递推关系 
    问题n 和 问题n-1之间关系 
    
    
    sum(100) = sum(99) + 100
                 |--- sum(98) + 99
                 
    sum(n) = sum(n-1) + n
    
  2.递归结束条件 
    n = 1 
    递归结束
    
//递归代码 
递归程序的代码逻辑 
int sum(int n)   //sum(n)                     
{                                                         
//判断是否到达递归结束条件             
//是 --- 则返回                                    
//否 --- 继续往下递归 (自己调用自己)
    if(n == 1)                                       
    {                                                    
        return 1;                                    
    }else //递归结束条件不满足          
    {                                                    
        return sum(n-1) + n;                 
    }                                                    
}                                                        

//递归的限制
1.层次不能太深 
2.有些问题的解决 --- 确实需要通过递归实现

使用递归方式实现n!,把上面程序中的加法改为乘法即可。

汉诺塔实现

二、数组 

1、数组 在函数中作为参数 

数组基础回顾

int a[10] = {1, 2, 3, 4};  // 初始化数组,剩余元素自动赋值为0
a[0] = 1;  // 数组元素是int型变量
a[1] = 2;

(1)数组元素作为函数参数

数组元素可以像普通变量一样作为函数参数,传递的是(值传递):

// 1. 数组元素作为函数参数
void printArrayEle(int ele) {
    printf("%d ", ele);
}

int main() {
    int a[10] = {1, 2, 3, 4, 5};
    printArrayEle(a[0]);  // 传递数组元素的值
    printArrayEle(a[1]);
    return 0;
}

(2)整个数组作为函数参数

1. 传递机制

// 2. 数组做函数参数
int main() {
    int a[10] = {1, 2, 3, 4, 5};
    printArray(a, 10);  // 传递数组名和长度
    return 0;
}

void printArray(int arr[], int size) {
    for(int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

2. 关键理解要点:

1. 传递的不是整个数组数据

  • 传递的是首元素地址(指针)

  • 实际上是地址传递,不是值传递

2. 为什么通过首地址可以访问所有元素

  • 连续性:数组元素在内存中连续存储

  • 单一性:所有元素类型相同,大小固定

  • 有序性:通过下标偏移量计算元素位置

  • 公式:元素地址 = 首地址 + 下标 * sizeof(元素类型)

3. 函数形参的两种等价写法

// 形式1:数组写法(推荐,可读性好)
void printArray(int a[], int size);

// 形式2:指针写法(本质)
void printArray(int *a, int size);

4. 重要注意事项

void printArray(int a[], int size) {
    // 错误!这里sizeof(a)得到的是指针大小(4或8字节)
    // 而不是数组总大小
    printf("sizeof(a) = %lu\n", sizeof(a));  
    
    // 正确:需要额外传递数组长度参数
    for(int i = 0; i < size; i++) {
        // 正确访问
    }
}

2.总结


  整个数组作为函数参数:
       形式上         
  形参               数组形式                 数组长度形参   
  实参         数组名(首元素地址)       传实际长度

二维数组:
  整型二维数组  做函数参数 
        
   形参     数组形式     行数         //形式上
  printArray(int a[3][4],int row);   //形式上
  printArray(int (*a)[4],int row);   //本质上 a起始还是指针变量 
  实参        数组名   实际行数 
  
  二维的本质 --- 一维数组的一维数组 
  int a[3][4];
  int[4] a[3];
  
  printArray(int[4] a[],int len);//从二维数组的本质考虑 

二维字符型数组:
  存放多个字符串 
  char s[3][10] = {"hello","world","china"};
  形参     数组形式      行数           //形式上
  printArray(char a[3][10],int row); //形式上
  printArray(char (*a)[10],int row); //本质上 a起始还是指针变量 
  实参      数组名      实际行数 
  

总结:
1.函数思想
2.函数定义
3.函数调用 
4.递归调用 
5.数组作为函数 
  一维字符数组做函数参数 
6.标识符的作用域 和 可见性问题 
  
  标识符 
     名字
        变量名
        函数名 
    
  作用域
      指的是标识符起效的范围 
      //局部作用域
      {   }      
      //全局作用域
       不在任何一个{   }范围内       
  可见性 
      站在程序角度看,执行到某句代码时,哪些标识符可见 
  
  int a=10;
  main()
  {
     int a=20; 
     printf("a = %d\n",a);     
  }

 标识符的可见性的规则:
1.先定义,后使用 
2.同一作用域中,不能有同名标识符
3.在不同的作用域,同名标识符,相互之间没有影响 
4.如果是不同的作用域,但是作用域之间存在嵌套关系,
 则内层的作用域的同名标识符,会屏蔽外层的作用域的同名标识符。
 (就近原则) 
 
 
变量:
   局部变量  
       放在 {} 范围内的 就是局部变量 
   全局变量 
       不在任何一个{} 范围内的,就是全局变量 
       
时间维度:
   变量的声明周期 
   (无到有再到无)
   
   局部变量 
      生命周期 
         当程序运行到对应的定义数据时,才创建 
         当程序运行到标识符,所在作用域结束时,销毁
   全局变量 
      生命周期
         从程序运行开始,就存在 
         到程序运行结束,销毁
         
存储类别的关键字 
auto   ---  数据存储在 上 ---自动申请 自动释放 
static ---  静态 表示数据存储在 静态区  
            静态区变量初始化, 不能用变量初始化,只能用常量初始化
            static 修饰的局部变量,值具有继承性,只需要初始化一次            
register  
extern

//五个区

内存分区:

  1. 栈区:自动变量,函数调用时分配,函数结束释放

  2. 堆区:动态分配内存(malloc/free)

  3. 静态区/全局区:全局变量和静态变量

  4. 字符串常量区:常量字符串

  5. 代码区:程序代码

 int main()
{
   auto int a;  //局部变量 --- 一般空间开在栈上
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐