云边日落 停杯投箸不能食,拔剑四顾心茫然。 (唐·李白·行路难)
歌曲封面 未知作品
  • 歌曲封面“魔女の宅急便”~ルージュの伝言松任谷由実

渝ICP备2021002886号-1

渝公网安备50022502000591号

网站已运行 3 年 319 天 15 小时 57 分

Powered by Typecho & Sunny

2 online · 45 ms

Title

函数

郭儿宝贝

·

Article
⚠️ 本文最后更新于2021年07月29日,已经过了1012天没有更新,若内容或图片失效,请留言反馈

函数是C语言的基本组成方式,如之前所遇到的main(),printf()等都是函数。

实例1 计算圆柱体积

输入圆柱体的高和半径,求圆柱体积,volume=πr2h。
要求定义和调用函数cylinder (r, h )计算圆柱体的体积。
代码如下:

♾️ c 代码:
# include <stdio.h>
double cylinder (double r, double h);    /* 函数声明*/
int main( void )
{
    double height, radius, volume;                    
    printf ("Enter radius and height: ");    
    scanf ("%lf%lf", &radius, &height);    
     /* 调用函数,返回值赋给volume */
    volume = cylinder (radius, height );    
    printf ("Volume = %.3f\n", volume);

    return  0;
}
/* 定义求圆柱体积的函数,用main()调用 */
double cylinder (double r, double h)   /*函数头*/    
{
    double result;

    result =3.1415926 * r * r * h;      /* 计算体积 */

    return result;                  /* 返回结果 */
}

运行结果:

♾️ c 代码:
Enter radius and height: 3.0  10
Volume = 282.743
函数定义

函数是指完成一个特定工作的独立程序模块。

  • 函数分类:
    库函数:由C语言系统提供定义,如 scanf ()printf () 等函数。
    自定义函数:需要用户自己定义,如计算圆柱体体积函数 cylinder ()
  • main ()也是一个函数,C程序由一个main ()或多个函数构成。
程序中一旦调用了某个函数,该函数就会完成特定的计算,然后返回到调用它的地方。

在自定义函数中,一般结构:

♾️ c 代码:
函数类型 函数名(形参表)    /* 函数首部,形参表后没有分号!!!*/
{                                                /* 函数体 */
    函数实现过程(代码)
    return 表达式;
}

在这里,函数返回值的类型与函数类型一致, return 表达式; 把函数运算的结果回送给主函数。

return只能返回一个值。

形参:

形参表:(类型1 参数1 ,类型2 参数2 ,……,类型n 参数n)

  • 参数之间用逗号分隔,每个参数前面的类型都必须分别写明。

(double r, double h) 不能写成 (double r,h)

函数的调用
  • 定义一个函数后,就可以通过程序来调用这个函数。
  • 调用标准库函数时,在程序的最前面用 #include 命令包含相应的头文件。
  • 调用自定义函数时,程序中必须有与调用函数相对应的函数定义。

    调用过程:

    以实例1为例:

  • 1.main()函数一直运行到:

    ♾️ c 代码:
    # include <stdio.h>
    double cylinder (double r, double h);    /* 函数声明*/
    int main( void )
    {
      double height, radius, volume;                    
      printf ("Enter radius and height: ");    
      scanf ("%lf%lf", &radius, &height);    
       /* 调用函数,返回值赋给volume */
      volume = cylinder (radius, height );    
      
    

    然后调用cylinder()函数,暂停main()函数,将radius和height的值传递给形参r和h;

  • 2.计算机转到cylinder()函数,形参r和h接受radius和height的值:

    ♾️ c 代码:
    double cylinder (double r, double h)   /*函数头*/    
    {
      double result;
    
      result =3.1415926 * r * r * h;      /* 计算体积 */
    
      
  • 3.函数cylinder()执行 return result ,结束函数运行,带着函数的结果result,返回到main()中调用它的地方;

    ♾️ c 代码:
    
      return result;                  /* 返回结果 */
    }
    volume = cylinder (radius, height );//返回到这一步
  • 4.从先前暂停的位置继续执行,将返回值赋给变量volume,输出体积:

    ♾️ c 代码:
    
      volume = cylinder (radius, height );    
      printf ("Volume = %.3f\n", volume);
    
      return  0;
    }
    

把调用其他函数的函数称为主调函数,如main();被调用的函数称为被调函数。

函数调用的形式

一般形式:
函数名(实际参数表)

  • 实际参数(实参)可以是常量、变量和表达式。
    对于实现计算功能的函数,函数调用通常出现在两种情况:
    赋值语句:

    ♾️ text 代码:
      volume = cylinder (radius, height );

    输出函数的实参:

    ♾️ text 代码:
      printf ("%f", cylinder (radius, height ) );

    参数传递

  • 两种参数:
    形参:函数定义时的参数被称为形式参数(简称形参),如:r和h,
    double cylinder (double r, double h);
    实参:函数调用时的参数被称为实际参数(简称实参),如:radius和height,
    volume = cylinder (radius, height);
  • 形参:用于接受实参传递过来的值,还可当做普通变量,是自定义函数里的;
  • 实参:一般是常量、变量或表达式,把值复制给形参,是主调函数里的;
  • 形参和实参必须一一对应:数量一致,类型一致,顺序一致

    程序运行遇到函数调用时,实参的值一次传给形参,即参数传递。

  • 如果实参是变量,它与所对应的形参是两个不同的变量。

    只与允许实参把值复制给形参

函数结果返回

return 表达式;
应先求解表达式的值,再返回其值。

  • return语句的作用:
    1.结束函数的运行;
    2.带着运算结果(表达式的值)返回主调函数。

    return语句只能返回一个值

函数原型声明

  • 函数必须先定义后调用,将主调函数放在被调函数的后面,就像变量先定义后使用一样。
  • 如果自定义函数在主调函数的后面,就需要在函数调用前,加上函数原型声明。
    函数原型声明:只写函数定义中的第1行(函数首部),并以分号结束。一般形式:
    函数类型 函数名(参数表);
    如:
♾️ c 代码:
double cylinder (double r, double h);
void pyramid (int n);
函数声明是一条C语句,而函数定义时的函数首部不是,后面不需要分号。

函数程序设计:

实例2:计算五边形的面积

将一个五边形分割成三个三角形,输入这些三角形的7条边长,计算该五边形的面积。
要求定义和调用函数area(x,y,z)计算边长为x、y、z的三角形面积。
代码如下:

♾️ c 代码:
# include <stdio.h>
double area(double x, double y, double z);/* 函数声明*/
int main( void )
{
  double a1, a2, a3, a4, a5, a6, a7,s;//定义五边形的7条边
  double area(double x, double y, double z);//函数调用
  printf("Please input 7 side lengths in the order a1 to a7:\n");
  scanf("%lf%lf%lf%lf%lf%lf%lf", &a1, &a2, &a3, &a4, &a5, &a6, &a7); 
  s = area(a1, a5, a6) + area(a4, a6, a7) + area(a2, a3, a7); /* 调用3次area */
  printf("The area of the Pentagon is %.2f\n", s) ;

  return 0;
}
/* 使用海伦-秦九韶公式计算三角形面积的函数 */
double area(double x, double y, double z)     /* 函数首部 */
{
  double p = (x + y + z) / 2;
    
  return sqrt(p * ( p - x ) * ( p - y ) * ( p - z )) ;          
}

运行结果:

♾️ c 代码:
Please input 7 side lengths in the order a1 to a7:
2.5 2.5 2.5 2.5 2.5 3.6 3.6
The area of the Pentagon is 10.47

实例3:使用函数判断完全平方数

定义函数IsSquare(n):当n为完全平方数时返回1,否则返回0。
1+3+5+7+......+(2×m−1)=m^2=n
代码如下:

♾️ c 代码:
/* 判断完全平方数的函数 */
int IsSquare (int n)        /* 函数首部 */
{  
    int i;

    for (i = 1; n > 0; i = i + 2) {
        n = n - i;
    }
    if (n == 0){
        return 1;         /* 是完全平方数返回1 */
    }else{
        return 0;         /* 不是完全平方数返回0 */
    }                            
}

实例4:使用函数求最大公约数

定义函数gcd(int m, int n):计算m和n的最大公约数
辗转相除法(欧几里得算法):
(1)r = m %n;
(2)若r为0,则返回n的值作为结果并结束;否则转第(3)步;
(3)m = n, n = r,返回第(1)步。

代码如下:

♾️ c 代码:
int gcd(int m, int n)
{  
   int r, temp;
     
   if(m < n){ 
       temp = m; 
       m = n; 
       n = temp;
   }
   r = m % n;
   while(r != 0){
       m = n;
       n = r;
       r = m % n;
    }
 
    return n;
}

实例5:使用函数判断素数

求100以内的全部素数,每行输出10个。素数就是只能被1和自身整除的正整数,1不是素数,2是素数。
要求定义和调用函数prime (m)判断m是否为素数,当m为素数时返回1,否则返回0。

代码如下:

♾️ c 代码:
#include <stdio.h>
#include <math.h>
int prime (int m);
int main(void)
{      
    int count, m;   
       count = 0;
    
    for(m = 2; m <= 100; m++){
        if ( prime(m) != 0   ){
            printf("%6d", m );     
            count++;                
            if (count %10 == 0)  
              printf ("\n");
         }
    }
    printf ("\n");
  
    return 0;
  
}
int prime (int m)
{   
    int i, n;
    if ( m <= 1 ) return 0;
    else  if ( m == 2 ) return 1;
    else{
        limit = sqrt (m) + 1;
        for( i = 2; i <= limit; i++){ 
             if (m % i == 0){
                    return 0;    
             }
        }
        return 1; 
    }
}

实例6:数字金字塔

代码如下:

♾️ c 代码:
/* 输出数字金字塔 */
#include <stdio.h>
void pyramid (int n);        /* 函数声明 */
int main (void)
{   
    pyramid(5);            /* 调用函数,输出数字金字塔 */
    return 0;
}
void pyramid (int n)                   /* 函数定义 */
{        
    int i, j;
    for (i = 1; i <= n; i++){        /* 需要输出的行数 */
        for (j = 1; j <= n-i; j++)     /* 输出每行左边的空格 */
        printf(" ");            
        for (j = 1; j <= i; j++)      /* 输出每行的数字 */ 
        printf("%d ", i);                  /* 每个数字的后面有一个空格 */ 
        putchar ('\n');                
    }
}
不返回运算结果的函数定义

类似上面的数字金字塔,不返回运算结果即表示不需要返回值,只需要输出结果即可。

  • 不返回结果的函数,在定义、调用、参数传递、函数声明上,思路完全与以前相同,只是函数类型变为void,以独立的调用语句方式:pyramid(n);

    函数类型为void,表示不返回结果,不可以省略;否则函数类型被默认定义为int。

    不返回运算结果的函数定义:

    ♾️ c 代码:
    void 函数名(参数表)      /* 函数首部 */
    {                                /* 函数体 */
      函数实现过程
      return;                  /* 可以省略return */
    }
    
  • 省略了return语句,不意味着函数不能返回,如pyramid()遇到最后一个大括号即自动返回主调函数。
结构化程序设计思想

强调程序设计的风格和程序结构的规范化,提倡清晰的结构。

  • 基本思路是将一个复杂问题的求解过程划分为若干阶段,每个阶段要处理的问题都容易被理解和处理。
  • 按自顶向下的方法对问题进行分析、模块化设计和结构化编码等3个步骤。

    自顶向下的分析方法

    把大的复杂的问题分解成小问题后再解决。

  • 面对一个复杂的问题,首先进行上层(整体)的分析,按组织或功能将问题分解成子问题。
  • 如果子问题仍然十分复杂,再做进一步分解,直到处理对象相对简单,容易处理为止。
  • 当所有的子问题都得到了解决,整个问题也就解决了。
    每一次分解都是对上一层的问题进行细化和逐步求精,最终形成一种类似树形的层次结构,来描述分析的结果。

模块化设计

  • 将模块组织成良好的层次系统
  • 模块用函数实现。
  • 一个模块只完成一个指定的功能。
  • 模块之间只通过带参数的函数进行调用。

    实例7:复数运算

    分别输入2个复数的实部与虚部,用函数实现计算2个复数之和与之积。

分析:
若2个复数分别为:

♾️ text 代码:
 c1=x1+y1i , c2=x2+y2i,

则:
c1+c2 = (x1+x2) + (y1+y2)i

♾️ text 代码:
c1*c2 = (x1*x2-y1*y2) + (x1*y2+x2*y1)i 

代码如下:

♾️ c 代码:
# include<stdio.h> 
double result_real, result_imag;  /*  全局变量,用于存放函数结果  */
/*  函数声明  */
void complex_prod(double real1, double imag1, double real2, double imag2);
void complex_add(double real1, double imag1, double real2, double imag2);
int main (void) 
{ 
    double imag1, imag2, real1, real2;    /* 两个复数的实、虚部变量 */
    printf ("Enter 1st complex number(real and imaginary): ");    
    scanf ("%lf%lf", &real1, &imag1);         /* 输入第一个复数 */
    printf ("Enter 2nd complex number(real and imaginary): ");    
    scanf ("%lf%lf", &real2, &imag2);     /* 输入第两个复数 */
    complex_add (real1, imag1, real2, imag2);    /* 求复数之和 */
    printf ("addition of complex is %f+%fi\n", result_real, result_imag);
    complex_prod (real1, imag1, real2, imag2); /* 求复数之积 */
    printf ("product of complex is %f+%fi\n", result_real, result_imag);
    return 0;
}

运行结果:

♾️ c 代码:
Enter 1st complex number(real and imaginary):1 1
Enter 2nd complex number(real and imaginary):-2 3
addition of complex is -1.000000+4.000000i
product of complex is -5.000000+1.000000i
局部变量和全局变量

局部变量:

  • 定义:在函数内定义的变量(包括形参);定义在复合语句内的变量。
  • 作用范围:本函数内部;复合语句内部。
  • 优点:最大程度确保了各函数之间的独立性,避免函数之间相互干扰。

全局变量:

  • 定义:在函数以外定义的变量,不从属于任一函数。
  • 作用范围:从定义处到源文件结束(包括各函数)。
  • 优点:对作用范围内所有的函数都起作用。
局部变量一般定义在函数或复合语句的开始处,标准C规定其不能定义在中间位置。
  • 同一范围(比如一个{}里)不可以重名,不同范围可以。
  • 不同范围里一旦重名,以最近的范围为主。

    一般情况下把全局变量定义在程序的最前面,即第一个函数的前面。

实例8:用函数实现财务现金记账。

先输入操作类型(1收入,2支出,0结束),再输入操作金额,计算现金剩余额,经多次操作直到输入操作为0结束。要求定义并调用函数,其中现金收入与现金支出分别用不同函数实现。
代码如下:

♾️ c 代码:
# include<stdio.h> 
double cash;        /* 定义全局变量,保存现金余额 */
void income (double number), expend(double number);    /* 函数声明  */
int main (void) 
{     
    int choice;
    double value;
    
    cash = 0;        /* 初始金额=0 */
    printf ("Enter operate choice(0--end, 1--income, 2--expend):");
    scanf ("%d", &choice);        /* 输入操作类型 */
    while (choice != 0){    /*  若输入类型为0,循环结束  */
        if (choice == 1 || choice == 2) {
        printf ("Enter cash value:");     /* 输入操作现金额 */
        scanf ("%f", &value); 
        if (choice == 1)     income(value);    /* 函数调用,计算现金收入 */
        else             expend(value);     /* 函数调用,计算现金支出 */
        printf ("current cash:%.2f\n", cash);
        }
        printf ("Enter operate choice(0--end, 1--income, 2--expend):");
        scanf ("%d", &choice);        /* 继续输入操作类型 */
    }
    return 0;
}
    /* 定义计算现金收入函数 */
void income(double number)
{    
    cash = cash + number;    /*  改变全局变量cash  */
}
    /* 定义计算现金支出函数 */
void expend(double number)
{    
    cash = cash - number;    /*  改变全局变量cash  */
}

运行结果:

♾️ c 代码:
Enter operate choice(0--end, 1--income, 2--expend):1
Enter cash value:1000
current cash:1000.000000
Enter operate choice(0--end, 1--income, 2--expend):2
Enter cash value:456
current cash:544.000000
Enter operate choice(0--end, 1--income, 2--expend):0
  • 过多使用全局变量会带来副作用,导致各函数间出现相互干扰。
  • 因此在变量使用中,应尽量使用局部变量和函数参数。
变量生命周期和静态局部变量

变量生命周期:

  • 定义:变量从定义开始分配存储单元,到运行结束存储单元被回收的整个过程。

    自动变量(auto): 普通的局部变量

    自动变量定义形式:
    auto 类型名 变量表; ,如:auto int x, y;auto char c1;

  • 函数调用时,定义变量,分配存储单元。
  • 函数调用结束,收回存储单元。
  • 自动变量如果没有赋初值,其存储单元中将是随机值。

    全局变量:

  • 定义:从程序执行开始,到程序的结束,存储单元始终保持。

静态变量

  • 静态变量定义格式:static 类型名 变量表 ,如 static int x;
  • 作用范围:局部变量
  • 生命周期:全局变量
  • 赋初值只在函数第一次调用时起作用,以后调用都按前一次调用保留的值使用。
  • 静态局部变量受变量作用范围限制,不能作用于其他函数(包括主函数)。
静态变量赋初值只在函数第一次调用时起作用,如果定义时没有赋初值,系统将自动赋0。

静态变量与全局变量均位于静态存储区

  • 共同点是生命周期贯穿整个程序执行过程。
  • 区别在于作用范围不同,
    全局变量可作用于所有函数,
    静态变量只能用于所定义函数,而不能用于其他函数。
现在已有 0 条评论,159 人点赞
Comment
发表
搜 索 消 息 足 迹
你还不曾留言过..
你还不曾留下足迹..
博主