第6章 函数

运行环境以Dev-C++、Visual Studio 2022、MacOS的命令行和Xcode为主

0.章节引言

  • 1.随着程序的膨胀和复杂化,将所有代码“堆积”到main()函数中不利于代码维护,把程序分解成不同的“功能段”会使工作简化、程序明晰,这些“功能段”即函数

  • 2.函数由实现一定任务的语句序列组成

// void:函数没有返回值
void display() {
    printf("hello!\n");
}    // 此函数执行结束后,回到main()函数

int main() {    // 先执行main函数
    display();    // 函数调用,数据流立即跳转到display()函数中
    return 0;
}
  • 3.一个C程序由一个或多个函数组成,函数之间可以相互调用,但不能调用main()函数

    • 标准函数,库函数:scanf() printf()

    • 用户自定义函数:display() add() sum()

1.函数的构成

  • 1.1 函数的定义格式
// 类型说明符:函数的返回值类型
// 形参列表:函数所需要的参数,在函数调用中传递数据。若函数不需要传递数据,可省略参数,但括号不能省略
// {} 内部是函数体,是该函数具体要执行的功能
类型说明符 函数名(形参列表) {
    声明部分;
    语句部分;
}

int sum(int a, int b) {
    int c = 0;    // 声明部分
    c = a + b;    // 语句部分
    return c;
}

// 若函数定义时省略"类型说明符",系统默认函数的返回类型为int。建议不要省略,以提升程序可读性
sum(int a, int b) {
    int c = 0;    // 声明部分
    c = a + b;    // 语句部分
    return c;
}
  • 1.2 函数的参数

    • 形式参数:在函数定义时指定的参数,简称形参,如下述代码max()函数的x和y

    • 实际参数:在函数调用时传递的数据,简称实参,如下述代码main()函数的a和b

    #include <stdio.h>
    int max(int x, int y) {  // 不可简写为 int max(int x, y)
        int z = 0;
        z = x > y? x:y;
        return z;
    }
    
    int main() {
        int a = 0, b = 0, c = 0;
    
        printf("input a、b:");
        scanf("%d %d", &a, &b);
    
        c = max(a, b);    // 函数调用,实参a的值传递给形参x,实参b的值传递给形参y,函数执行结束后的返回值传递给c
        printf("较大值: %d", c);
        return 0;
    }
    
    • 注意事项

      • 1.函数的形参可以有多个,用,分隔,类型相同或不同均可。相同类型时不可随意简写

      • 2.形参必须是变量,实参可以是变量、常量和表达式

      • 3.实参类型必须与形参类型兼容,在符合数据类型转换规则的前提下,系统会按照赋值转换规则进行类型转换,如float转为intchar转为int

      • 4.函数进行普通“值传递”时,将实参值传递给形参,传递方向为单向。传递结束后,若在函数中修改形参的值,并不会反作用于实参,两者在内存中分别占用不同的内存单元

  • 1.3 函数的返回值

    • 若希望获取函数调用后的执行结果,并在main()函数中使用,则函数必须使用return语句将结果返回给调用函数

    • 一般格式:return (表达式); 或 return 表达式;

    • 注意事项

      • 1.一个函数可以有多条return语句,分别为指定条件返回各自的值。执行完第1个return语句后便终止函数的执行,将结果返回给调用函数并继续执行调用函数中的语句

      • 2.函数默认的返回类型为int,可省略函数类型说明符,但不建议这么做

      • 3.若return的类型和函数类型不一致,以函数类型说明符为准

      • 4.若函数没有return语句,则返回一个不确定的值。建议需要函数返回值时就加上函数的类型,不需要返回值时定义函数类型为void

2.函数的调用与声明

  • 2.1 函数调用

    • 一般格式:函数名(实参列表)

    • 注意事项

      • 1.()是函数调用操作符。若函数无参数,则括号内为空,但不能省略

      • 2.多个参数之间使用逗号分隔

      • 3.实参个数与形参相同,且类型赋值兼容

    • 调用方式

      • 1.函数语句
      // 函数有或没有返回值都可以这样调用
      max(a, b);
      calcu(n);
      
      // 该调用方法只执行函数中的语句,不使用返回值
      
      • 2.函数表达式
      // 函数有返回值才可以这样调用,将函数的调用作为一个值参与表达式运算
      c = 2 * max(a, b) + 5;
      
      // void型函数不允许使用这种方式
      
      • 3.作为函数参数
      // 函数有返回值才可以这样调用,将函数的调用作为函数实参传递给函数形参
      d = max(max(a, b), c);
      
      // void型函数不允许使用这种方式
      
    • 案例分析

      • 1.输入 n 的值,计算1 ~ n 的整数和
      #include <stdio.h>
      
      int calcu(int x) {    // 求和函数
          int i = 0, total = 0;
          for(i = 1; i <= x; i++) {
              total += i;
          }
          return total;
      }
      
      int main() {
          int n = 0;
          scanf("%d", &n);
      
          printf("1+2+3+...+%d = %d", n, calcu(n));    // 传递n的值给形参x
          return 0;
      }
      
  • 2.2 函数声明

    • 一般格式:类型说明符 函数名(形参列表);

    • 作用:令编译器在编译时对函数调用检查合法性,非法函数调用会编译报错

    • 注意事项

      • 1.为提高程序可读性,建议把函数原型写在main()函数前,函数定义写在main()函数后。若函数定义在main()函数前,可省略函数原型

      • 2.函数声明也称为函数原型,可写在主调函数中

      int main() {
          int calcu(int x);    // 函数声明
      
          int n = 0;
          scanf("%d", &n);
          printf("1+2+3+...+%d = %d", n, calcu(n));
          return 0;
      }
      
      • 3.函数声明中可省略形参名称,只写形参类型,如int max(int, int);编译系统不检查形参名
  • 2.3 函数嵌套调用与递归调用

    • 递归调用:函数调用自身,必须有条件地进行(递归终止条件),否则无限递归会导致程序崩溃

    • 注意事项

      • 1.C程序中函数的定义不能嵌套,但函数的调用可以嵌套

      • 2.main()函数是程序执行的开始,一般不会像其他自定义函数显式调用,而是由编译器进行隐性调用

      • 3.递归程序的关键在于找出递归关系和递归终止条件

      • 4.递归算法容易理解,但递归有可能大大增加程序运行时间并消耗大量内存

    • 案例分析

      • 1.输入正整数 n 的值,求n!
      // 当n = 0或1时,n! = 1;当n >= 1时,n! = n * (n - 1)!
      
      #include <stdio.h>
      
      float fac(int n) {
          float f = 0.0f;
          if(n <= 1) {    // 避免无限递归
              f = 1;
          }
          else {
             f = n * fac(n - 1);
          }
          return f;
      }
      
      int main(){
          int n = 0;
          float result = 0.0f;
      
          printf("input an integer:");
          scanf("%d", &n);
      
          result = fac(n);    //   求n!
          printf("result=%.2f\n", result);    // 输出计算结果
          return 0;
      }
      

      • 2.输入正整数 n,求 Fibonacci 序列第 n 项的值
      // 当n = 1或2时,fib(n) = 1;当n > 2时,fib(n) = fib(n - 1) + fib(n - 2)
      
      #include <stdio.h>
      
      int cnt = 0;    // 全局变量,用于统计递归函数调用的次数
      int fib(int n) {
          cnt++;
          if(n <= 2) {
              return 1;
          }
          else {
              return fib(n - 1) + fib(n - 2);
          }
      }
      
      int main(){
          int n = 0, result = 0;
      
          scanf("%d", &n);
          result = fib(n);
          printf("FIB(%d) = %d\n", n, result);
          printf("递归次数=%d\n", cnt);
          return 0;
      }
      
      • 3.递归经典问题:汉诺塔
      #include <stdio.h>
      
      int hanoi(int n, char x, char y, char z){  // n:塔的数量 a、b、c位柱子的名称
          if(n > 0){
              hanoi(n-1, x, z, y);
              printf("moving from %c to %c\n", x, z);
              hanoi(n-1, y, x, z);
      }
          return 0;
      }
      
      int main(){
          int num = 0;
          char a = 0, b = 0, c = 0;
      
          printf("请输入塔的数量和三个柱子的名称:\n");
          scanf("%d %c %c %c", &num, &a, &b, &c);
      
          hanoi(num, a, b, c);
          return 0;
      }
      

3.数组作为函数参数

  • 3.1 数组元素作为函数参数

    • 值传递:数组元素作为函数参数传递的本质是传递该元素的值,与普通变量作为参数的行为类似,函数内部对参数的修改不会影响原始数组中的元素

    • 案例分析

      • 1.找出数组中的最大值
      #include <stdio.h>
      
      int max(int x, int y) {    // 计算两数较大值的函数
          return x > y? x : y;
      }
      
      int main() {
          int a[10] = {0};
          int i = 0, m = 0;
      
          printf("input 10 integers:\n");
          for (i = 0; i < 10; i++) {
              scanf("%d", &a[i]);
          }
      
          m = a[0];  // 假设下标为 0 的元素为最大值,后续遍历下标为 1 ~ N - 1 的元素
          for (i = 1; i < 10; i++) {
              m = max(m, a[i]);  // 调用函数
          }
          printf("max is %d", m);
      
          return 0;
      }
      
  • 3.2 数组名作为函数参数

    • 地址传递

      • 因为数组名表示数组的首地址,所以实参传递地址给形参,形参和实参所代表的数组共享同一段内存单元

      • 若在函数中改变数组元素值,则修改了内存单元中存储的数据,实参数组对应元素值也会被修改

      • 编译器只关心数组的首地址,不关心数组长度,因此会加一个函数形参来接收数组长度

    • 案例分析

      • 1.编写一个函数显示数组的值
      #include <stdio.h>
      
      void display(int b[], int n) {
          int i = 0;
          for (i = 0; i < n; i++) {
              printf("%4d", b[i]);
          }
      }
      
      int main() {
          int a[5] = {10, 20, 30, 40, 50};
      
          display(a, 5);  // 数组名作为实参
          return 0;
      }
      
  • 3.3 多维数组作为函数参数

    • 注意事项

      • 数组名作为函数参数传递时传递的是数组的首地址

      • 函数形参若是二维数组,可省略第一维的大小,不能省略第二维的大小,否则函数无法确定所传数组的行列数

      • 实参数组可以与形参数组的第一维大小不同,在函数形参中常添加一个整型参数表示二维数组第一维的大小

    • 案例分析

      • 1.将一个 4 * 4 的矩阵转置
      // 分析:矩阵转置就是第i行第j列的元素与第j行第i列的元素交换
      
      #include <stdio.h>
      
      void reverse(int b[][4], int n) {    // 实现矩阵转置
          int i = 0, j = 0, temp = 0;
      
          for (i = 0; i < n; i++) {
              for (j = 0; j <= i; j++) {    // 只遍历数组主对角线及左下角的元素
                  temp = b[i][j];
                  bi[][j] = b[j][i];
                  b[j][i] = temp;
              }
          }
      }
      
      void printfarr(int a[][4], int n) {    // 实现打印数组元素
          int i = 0, j = 0;
      
          for(i = 0; i < 4; i++) {
              for(j = 0; j < 4; j++) {
                  printf("%4d", a[i][j]);
              }
              printf("\n");
          }
      }
      
      int main() {
          int a[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
      
          printf("输出转置前的矩阵:\n");
          printfarr(a, 4);
      
          reverse(a, 4);
      
          printf("\n输出转置后的矩阵:\n");
          printfarr(a, 4);
      
          return 0;
      }
      

4.变量的作用域

  • 4.1 局部变量

    • 定义:在函数内部或代码块中({}内部)声明的变量,包括函数的形参

    • 作用域:变量所在的局部范围仅在包含该变量声明的函数中才起作用,在函数外不能使用

    • 注意事项

      • 1.局部变量的生命周期为从定义开始,到函数或代码块结束

      • 2.在程序的各个部分允许使用同名变量,实参和形参名也允许同名,如案例分析3

      • 3.main()函数中的变量也是局部变量

      • 4.允许在复合语句(分程序或程序块)中定义变量,它仅在复合语句内有效,如案例分析1

    • 案例分析

      • 1.分析以下代码的执行情况
       #include <stdio.h>
      
      int main(int argc, const char * argv[]) {
          int i = 0;    // i 的作用域是整个 main() 函数
          for (i = 0; i < 3; i++) {
              ;
          }
          printf("%d\n", i);
      
          return 0;
      }
      
      // 可正常编译运行
      
      #include <stdio.h>
      
      int main(int argc, const char * argv[]) {
          for (int i = 0; i < 3; i++) {
              ;
          }
          printf("%d\n", i);    // 超出了i的作用域
      
          return 0;
      }
      
      // 编译报错,提示变量 i 未声明
      // 变量 i 在for循环中声明,因此其生效范围仅在 for 循环中
      

      image

      • 2.分析以下代码的执行情况
      #include <stdio.h>
      
      void local_value() {
          int a = 1, b = 2;    // a、b的作用域仅限于local_value函数中
          printf("%d %d", a, b);
      }
      
      int main(int argc, const char * argv[]) {
          printf("%d %d", a, b);
      
          return 0;
      }
      
      // 编译报错,main() 函数中并未定义变量 a、b
      

      • 3.分析以下代码的执行情况
      #include <stdio.h>
      
      void local_value() {
          int a = 1, b = 2;    // a、b的作用域仅限于local_value函数中
          printf("%d %d", a, b);
      }
      
      int main(int argc, const char * argv[]) {
          int a = 5;    // a、b的作用域仅限于main()函数中
          float b = 2.5;
          printf("%d %f\n", a, b);
      
          return 0;
      }
      // 可正常编译运行
      
    • 函数的地址

      • 定义:函数名本质上是指向函数代码起始地址的常量指针,可通过打印函数名或使用函数指针来获取函数的地址

      • 与变量作用域的关系:正是因为不同函数在内存中的地址不同,即占用的内存空间不同,所以才允许在不同函数中使用相同的变量名

      • 案例分析

      #include <stdio.h>
      
      void local_value() {
          int a = 1, b = 2;
          printf("%d %d", a, b);
      }
      
      int main(int argc, const char * argv[]) {
          int a = 5;
          float b = 2.5;
          printf("main函数的地址为: %p\n", (void *)main);    // 将函数指针强制转换为void*类型以匹配%p格式说明符
          printf("local_value函数的地址为: %p\n", (void *)local_value);
      
          return 0;
      }
      

  • 4.2 全局变量

    • 定义:在函数外部({}外部)定义的变量,也称外部变量或全程变量

    • 作用域:整个工程;从定义位置到文件结束,在这个范围的任何地方都可使用,包括函数内

    • 注意事项

      • 1.全局变量的生命周期即整个程序的生命周期

      • 2.全局变量的作用域使得其值容易修改,降低了程序的可读性,容易产生逻辑错误

        • 模块之间高耦合度导致某个模块的修改牵涉到其他模块修改,增加了代码的脆弱性和复杂性
      • 3.允许局部变量和全局变量重名但不建议这么做,在局部变量作用域内全局变量被"屏蔽"

      • 4.程序设计要满足"高内聚,低耦合",全局变量破坏了这一要求

    • 案例分析

      • 1.分析以下代码的执行情况
      #include <stdio.h>
      int a = 5, b = 10;    // 全局变量a、b
      
      void modify() {
          int a = 5, b = 10;    // 局部变量a、b,此时全局变量a、b被屏蔽
          a++;
          b--;
          printf("pos1: a=%d, b=%d\n", a, b);    // a的值为 6,b的值为 9
      }
      
      int main(int argc, const char * argv[]) {
          modify();
          printf("pos2: a=%d, b=%d\n", a, b);    // a的值为 5,b的值为 10
      
          return 0;
      }
      

5.变量的存储类别

  • 5.1 存储方式

    • 时空分析

      • 时间角度:生存期,静态存储方式和动态存储方式

      • 空间角度:作用域,局部变量和全局变量

    • 内存划分

      • 栈区

        • 存放局部变量和函数调用时的上下文信息,如现场保护、返回地址等

        • 容量有限,递归过深或局部变量过大都可能会导致栈溢出

      • 堆区

        • 程序运行时动态分配的内存(通过 malloc/calloc/realloc 申请)

        • 适合存储生命周期不确定的数据,如链表、动态数组

        • 程序员负责申请和释放(free)内存,忘记释放会导致内存泄露

      • 静态区

        • 存放静态变量和全局变量

        • 程序执行期间始终占据固定存储单元

    • 变量属性

      • 数据类型:定义变量的取值范围和允许的操作

      • 存储类别:数据在内存中存储分配和释放的时机以及存储结构

  • 5.2 自动变量

    • 定义:函数中的局部变量未加static声明就属于自动变量,包括函数内的局部变量、函数形参和复合语句中的变量

    • 注意事项

      • 1.调用函数时为自动变量分配内存,调用结束后释放内存

      • 2.声明关键字为auto,如auto int z;,可省略auto

      • 3.C 标准(如 C89、C99、C11)明确规定,函数形参列表中不能显式使用存储类别说明符,如auto

  • 5.3 静态变量

    • 1.静态局部变量

      • 定义:使用static关键字修饰的局部变量

      • 特征:属于静态存储类型,在静态存储区分配内存;程序运行之初就分配内存,而非函数调用时,运行结束后释放

      • 适用场合

        • 1.保留调用后的变量值

        • 2.确定某函数的调用次数

      • 注意事项

        • 1.未初始化的静态局部变量系统自动为其初始化为0

        • 2.静态局部变量的初始化在编译时进行,可通过汇编代码查看,静态局部变量的部分没有汇编代码。也可以分步调试查看,调试期间会自动跳过静态变量所属语句

        • 3.静态局部变量在程序运行期间都存在,作用域仍为函数内部

        • 4.静态局部变量长期占用内存,应尽量少用

      • 案例分析

        • 1.分析以下代码的执行情况
        #include <stdio.h>
        
        int i = 0;
        
        void sub() {
            int a = 0;
            static int b = 0;    // 静态局部变量b,保留上一次函数调用后的值
            a++;
            b++;
            printf("%d: a=%d, b=%d\n", i, a, b);
        }
        
        int main() {
            for(i = 1; i <= 3; i++) {
                sub();
            }
        
            return 0;
        }
        // 1: a=1, b=1
        // 2: a=1, b=2
        // 3: a=1, b=3
        
    • 2.静态全局变量

      • 全局变量的外部链接属性变成了内部链接属性

      • 其他.c源文件不能再使用该变量,使用时感觉作用域变小

    • 3.静态函数

      • 定义:用static关键字修饰的函数

      • 特征:只能在定义它的源文件内部被调用,其他源文件无法对其调用,即便其他文件使用extern声明也不行

      • 注意事项

        • 不同源文件有同名的静态函数并不会引发冲突,因为它们的作用域相互独立

        • 静态函数具有内部链接属性,它不会被链接器导出,不会和其他文件中同名的函数产生冲突

  • 5.4 寄存器变量

    • 定义:存储在CPU的寄存器中的变量,用register修饰

    • 作用:访问寄存器的速度远高于访问内存的速度,因此能够提高程序运行效率

    • 注意事项

      • 1.只有局部变量和形式参数才允许声明为寄存器变量,通过不断分配与释放寄存器可提高寄存器利用率

      • 2.寄存器变量的数量受到计算机寄存器数目的限制

      • 3.多数系统只允许定义int、char和指针类型变量为寄存器变量;多数系统将register作为自动变量处理,存储在内存中

      • 4.若部分未定义为寄存器类别的变量使用频繁,编译系统自动将之存放在寄存器中

  • 5.5 外部变量

6.函数应用实例

  • 1.将序列的最大值和第 1 个数交换,最小值和最后一个数交换,并输出新序列
// 分析:遍历数组,定位最大值和最小值的下标,完成交换
// 注意1:代码通过最值的下标定位到最值,而不是直接记录最值的数值,因为数组中对元素的引用借助下标实现
// 注意2:第1个数恰好是最小值的特殊情况

#include <stdio.h>

void exchange(int b[], int n) {    // 完成交换的核心代码
    // max 为最大值下标,min 为最小值下标,初值均为0,即假设下标为 0 的数为最大/小值
    int max = 0, min = 0;
    int i = 0, temp = 0;
    
    for (i = 1; i < n; i++) {
        if (b[i] > b[max]) {
            max = i;
        }
        if (b[i] < b[min]) {
            min = i;
        }
    }
    printf("最大值为 %d, 最小值为 %d\n", b[max], b[min]);

    // 最大值和下标为 0 的数交换
    temp = b[0];
    b[0] = b[max];
    b[max] = temp;
    
    // 若下标为 0 的位置恰好是最小值,它被替换到了下标为 max 的位置
    if (0 == min) {
        min = max;
    }
    
    // 最小值和下标为 n - 1 的数交换
    temp = b[n - 1];
    b[n - 1] = b[min];
    b[min] = temp;
}

void print_arr(int a[], int n) {    // 完成打印数组元素的核心代码
    int i = 0;
    while (i < n) {
        printf("%4d", a[i]);
        i++;
    }
    printf("\n");
}

int main() {
    int a[10] = {-5, 3, 8, 9, -1, -3, 5, 6, 0, 4};
    int i = 0;
    
    printf("交换前: ");
    print_arr(a, 10);
    exchange(a, 10);
    printf("\n交换后: ");
    print_arr(a, 10);
}
  • 2.编写一个函数,在一个整数序列中查找某个整数,若存在,返回该整数在序列中的位置;否则返回 -1 。要求在主函数中调用,输出结果
// 分析:遍历数组,逐个对比当前数组元素与目标值看是否相等
// 本题可延伸至二分查找,具体参考案例分析4

#include <stdio.h>

int search(int a[], int n, int target) {    // 实现查找的核心代码
    int i = 0;

    for (i = 0; i < n; i++) {
        if (a[i] == target) {
            return i;    // 找到目标,返回下标
        }
    }
    return -1;    // 未找到目标
}

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);
    int target = 30;

    int result = search(arr, size, target);
    
    if (result != -1) {
        printf("目标整数 %d 在数组中的位置是: %d\n", target, result);
    }
    else {
        printf("目标整数 %d 不在数组中\n", target);
    }

    return 0;
}
  • 3.编写一个函数,将字符串转换成相应整数。转换时,遇到非数字字符停止转换。要求在主函数中调用,输出结果。例如:"123"——>123;"12a3"——>12;"a123"——>0
#include <stdio.h>
#include <ctype.h>

int convert(char s[]) {
    int result = 0;
    int i = 0;
    
    // 跳过前导空格
    while (s[i] == ' ') {
        i++;
    }
    
    // 处理正负号(默认为正数)
    int sign = 1;
    if (s[i] == '-') {
        sign = -1;
        i++;
    }
    else if (str[i] == '+') {
        i++;
    }
    
    // 转换数字字符,遇到非数字则停止
    while (isdigit(s[i])) {
        result = result * 10 + (s[i] - '0');
        i++;
    }
    
    return sign * result;
}

int main() {
    char test1[] = "123";
    char test2[] = "12a3";
    char test3[] = "a123";
    char test4[] = "   -456xyz";
    char test5[] = "   +789";
    
    printf("%s -> %d\n", test1, convert(test1));
    printf("%s -> %d\n", test2, convert(test2));
    printf("%s -> %d\n", test3, convert(test3));
    printf("%s -> %d\n", test4, convert(test4));
    printf("%s -> %d\n", test5, convert(test5));
    
    return 0;
}
  • 4.现有一按升序排列的数组 {3, 9, 10, 13, 16, 27, 30},输入正整数x,使用二分查找的方式查询它是否在数组中
//  代码1

#include <stdio.h>

int main(int argc, const char * argv[]) {
    // insert code here...
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int k = 17, sz = sizeof(arr) / sizeof(arr[0]);
    int left = 0, right = sz - 1;
    
    while (left <= right) {     // 中间有元素被查找,要带等于
//      int mid = (left + right) / 2;    // mid的求取在循环内
        int mid = left + (right - left) / 2;    // 避免left + right越界溢出,将right比left多的部分的一半再加到left上,这样两者就一样了
        if (arr[mid] < k) {
            left = mid + 1;
        }
        else if (arr[mid] > k) {
            right = mid - 1;
        }
        else {
            printf("找到了, 下标是: %d\n", mid);
            break;
        }
    }
    if (left > right) {
        printf("找不到\n");
    }
    return 0;
}

// 代码2
#include <stdio.h>
int main(){
    int a[7] = {3, 9, 10, 13, 16, 27, 30};
    int left = 0, right = 6;
    int mid = 0, x = 0;

    printf("请输入要查找的数:\n");
    scanf("%d", &x);

    while(left <= right){
        mid = (left + right) / 2;
        if(a[mid] > x){
            right = mid - 1;
        }
        else if(a[mid] < x){
            left = mid + 1;
        }
        else{
            printf("查找的数据在第%d个\n", mid + 1);
            return mid;
        }
    }
    printf("查找的数据不存在!\n");
    return 0;
}

// 代码3
#include <stdio.h>

int binary_sort(int s[], int n, int x){
    int left = 0, right = n - 1, mid;

    while(left <= right){
        mid = (left + right) / 2;
        if(s[mid] > x){
            right = mid - 1;
        }
        else if(s[mid] < x){
            left = mid + 1;
        }
        else{
            return mid;
        }
    }
    return -1;
}

int main(){
    int a[7] = {3, 9, 10, 13, 16, 27, 30};
    int index = 0, x = 0;

    printf("请输入要查找的数:\n");
    scanf("%d", &x);

    index = binary_sort(a, 7, x);
    if(index >= 0){
        printf("要查找的 %d 下标为 %d\n", x, index);
    }
    else {
        printf("查找的数据不存在!\n");
    }
    return 0;
}
posted @ 2025-03-06 09:32  pycoder_666  阅读(71)  评论(0)    收藏  举报