C语言之函数

  结构化程序设计主张按功能来分析需求,主要原则自顶向下,逐步求精,模块化等。

主张按功能把软件系统逐步细分,每个功能都负责对数据进行一次处理,每个功能接收一些数据,处理完后输出一些数据,这种处理方式也被称为面向数据流的处理方式。

其最小单位是函数,每个函数负责一个功能,整个软件系统由一个个函数组成,其中作为程序入口的函数被称为主函数。

主函数依次调用其他普通函数,普通函数之间依次调用,从而完成整个软件系统的功能。

  一个C程序可由一个主函数和若干个其他函数构成,主函数调用其他函数,其他函数也可互相调用。

  函数定义的一般形式:(无参函数/有参函数/空函数)

无参函数形式:

类型标识符  函数名()

{

声明部分;

语句部分;

}

有参函数形式:

类型标识符  函数名(形式参数列表

{

声明部分;

语句部分;

}

空函数形式:在程序开发初始阶段,可以在将来准备扩充功能的地方写上一个空函数,采用实际函数名,只是这些函数未编好,先占一个位置,以后用一个编好的函数代替它。

类型说明符  函数名()

{

}

  函数参数和函数的值

形参与实参

定义函数时,函数名括号里的参数为形参;

调用函数时,被调用函数括号里的为实参;

关于形参与实参的说明:

1)在定义函数中指定的参数,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时才分配内存单元。调用结束后,形参占用的内存也被释放。

2)实参可以是常量,变量或表达式。

3)在被定义的函数中,必须指定形参的类型。

4)实参与形参的类型应相同或兼容。

5)实参向形参传递数据,“值传递”,单向的

6)返回值由return语句获得。如果函数值的类型与return语句中的表达式不一致,则以函数类型为准。即函数类型觉得返回值的类型。

7)对于不带返回值的函数,用void定义函数为无类型(空类型);

  函数调用:按函数在程序中出现的位置来分,可以分为三种调用方式。

  函数的嵌套调用

C语言不可嵌套定义函数,函数之间是平等的,但可以嵌套调用函数。

//用截距法求方程 
#include<stdio.h>
#include<math.h> 
//define function f
float f(float x)
{
    float y;
    y=((x-5)*x+16)*x-80;
    return y;
}
//define function of cross point with x: xpoint
float xpoint(float x1,float x2)
{
    return ((x1*f(x2)-x2*f(x1))/(f(x2)-f(x1)));
}
// define root function
float root(float x1,float x2)
{
    float x,y,y1;
    y1=f(x1);
    do{
        x=xpoint(x1,x2);
        y=f(x);
        if(y*y1>0)
        {
            y1=y;
            x1=x;
        }
        else
            x2=x;
    }while(fabs(y)>=0.00001);
    return x;
}
int main()
{
    float x1,x2,f1,f2,x;
    do
    {
        printf("input x1,x2:\n");
        scanf("%f %f",&x1,&x2);
        f1=f(x1);
        f2=f(x2);
    }
    while(f1*f2>=0);
    x=root(x1,x2);
    printf("A root of equation is %f\n",x);
}

  函数的递归调用

在定义一个函数的过程中有出现直接或间接地调用该函数本身,称为函数的递归调用。

int f(int x)
{
  int y,z;
  z=f(y);
   return z;    
}
//求阶层
#include<stdio.h>

float frac(int n)
{
    float f;
    if(n<0)
    {
        printf("error");
    }
    else
        if(n==0||n==1)
        f=1;
    else
    f=frac(n-1)*n;
    return f;    
 } 
 
 int main()
 {
    float y;
    int n;
    printf("input an integer:");
    scanf("%d",&n);
    y=frac(n);
    printf("%d!=%10f\n",n,y);
 }

  数组作为函数参数

数组名可以做形参和实参,传递数组首地址;

#include<stdio.h>

float average(float a[10])
{
    int i;
    float aver,sum=0;
    for(i=0;i<10;i++)
    {
        sum=a[i]+sum;
    }
    aver=sum/10;
    return aver;
}

int main()

{
    float score[10],aver;
    int i;
    printf("input 10 scores:\n");
    for(i=0;i<10;i++)
    scanf("%f",&score[i]);
    printf("\n");
    aver=average(score);
    printf("%f\n",aver);
}

说明:1)用数组名做函数参数,应该在主调函数和被调函数分别定义数组;例如score,a,数组。

 2)实参数与形参类型一致。

 3)在被调用函数中声明了形参数组大小为10,但在实际上,指定其大小是不起任何作用的,因为C语言编译对形参数组大小不做检查,只是将实参数组的首元素的地址传给形参数组。因此,形参数组名获得了实参数组的首元素地址。它们共占统一地址,同一存储单元。score[n]和a[n]具有相同的值。

 4)形参数组可以不指定大小,在定义数组时在数组名后面跟一个空的括号。也可设置另一个形参。

5)用数组名作函数实参时,不是把数组元素的值传递给形参,而是把实参数组的元素的地址传递给形参数组。这样两个数组共占用一段内存单元。

  局部变量和全局变量:从变量的作用域(空间)角度来分。

局部变量:在一个函数内部定义的变量是内部变量,只在函数范围内有效。本函数才能使用它们。

1)主函数中定义的变量(m,n)也只在主函数中有效,主函数也不能使用其他函数中定义的变量。

2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。

3)形参也是局部变量。

4)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效。

全局变量

对应于局部变量,在函数外部定义的变量称为外部变量,全局变量。其他函数可以调用。

全局变量增加了函数间数据联系的渠道。一般将全局变量名的第一个字母大写。

1)全局变量在程序的全部执行过程都占用内存单元,而不是仅在需要时开辟单元。

2)它使函数的通用性降低了,因为函数在执行时要依赖于其所在的外部变量。

3)使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬间各个外部变量的值。因此,要限制外部变量。

4)如果同一源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。

    变量的存储类别

动态存储方式与静态存储方式(从变量值存在的时间(生存期))

静态存储方式:在程序运行期间由系统分配固定的存储空间的方式。

动态存储方式:在程序运行期间根据需要进行动态分配存储空间的方式。

全局变量全部放在静态存储区,在程序开始时分配空间,占据固定的内存单元。程序执行结束释放内存。

动态存储区存放以下数据:

1)函数形参

2)自动变量(auto)

3)函数调用时的现场保护和返回值等

以上数据在函数调用开始时分配动态内存空间,函数结束释放。如果一个程序调用两次同一函数,分别给函数形参不同的存储空间。

变量有两个属性:存储类型(数据在内存中存储的方式:动态和静态)数据类型

具体包括四种:

  auto变量

如果不专门声明为static来存储类别,都是动态分配存储空间的。

  用static声明局部变量

有时希望函数中局部变量的值在函数调用后不消失而保留原值,即其占用内存单元不释放,在下一次调用该函数时,该变量已经有值,即上一次函数调用结束时的值。

1)静态句变量属于静态存储类别,在静态存储区分配空间。整个运行过程不释放内存。

2)只赋初值一次。

3)如果定义局部变量时不赋值的话,则对静态局部变量来说,编译时自动赋初值0或空字符;

对自动变量而言,不赋初值则分配一个不确定的值。

4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。

  register寄存器变量

c语言语序将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取,这样提高效率。用关键字 register声明。

1)只有局部自动变量和形参可以作为寄存器变量,其他(全局)不行。

2)由于计算机中寄存器数目有限,不能定义任意多个寄存器变量;

3)局部静态变量不能定义为寄存器变量;

  用extern声明外部变量

外部变量时在函数的外部定义的全局变量,它的作用域时从变量的定义处开始,到本程序文件的尾部。编译时分配在静态存储区。有时需要用extern声明外部变量,以扩展外部变量的作用域。

  用static声明外部变量

在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用;

在程序设计过程中,常由若干人分别完成不同的各个模块,各人可以独立地在其设计的文件中使用相同的外部变量名而互不相干。只需要在每个文件的开头加上static声明即可。

  内部函数和外部函数

函数本质上是全局的,因为一个函数要被另外的函数调用,但也可以指定函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数分为内部函数和外部函数;

  内部函数(静态函数)

如果一个函数只能被本文件中的其他函数所调用,它称为内部函数。函数的作用域只局限于所在文件,在不同的文件中有同名的内部函数,互不干扰。在函数名和函数类型前面加static;

static int fun(int a, int b);

  外部函数

1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数时外部函数,可供其他函数调用。默认为外部函数。

2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏web前端

Vuejs --04 计算属性

一、使用原因      1、模板中表达式很便利,但实际上只适用于简单的运算,不适宜放入太多逻辑运算,例如: <div id="example"> {{ mess...

2077
来自专栏Micro_awake web

JavaScript(五):函数(闭包,eval)

1.函数的申明:三种方法: function命令 函数表达式:变量赋值 Function构造函数 1 //method 1: function命令 2 fu...

23910
来自专栏nimomeng的自我进阶

Block官方文档

a) 像函数一样有定义好的参数 b) 有返回值 c) 能从定义的作用域中捕获状态(值) d) ...

1192
来自专栏炉边夜话

java 异常处理学习笔记

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

1062
来自专栏破晓之歌

JAVA入门1 原

下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848...

1462
来自专栏青玉伏案

算法与数据结构(十五) 归并排序(Swift 3.0版)

上篇博客我们主要聊了堆排序的相关内容,本篇博客,我们就来聊一下归并排序的相关内容。归并排序主要用了分治法的思想,在归并排序中,将我们需要排序的数组进行拆分,将其...

1975
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(六) ——Redis中的压缩列表

《Redis设计与实现》读书笔记(六) ——Redis中的压缩列表 (原创内容,转载请注明来源,谢谢) 一、概述 压缩列表(ziplist)是列表键(list)...

3957
来自专栏吾爱乐享

java之学习vector类的特有功能

1302
来自专栏java工会

Java基础第一阶段知识点,招实习的面试官都在问这些

a) 答:Java源文件被编译成字节码的形式,无论在什么系统环境下,只要有java虚

1361
来自专栏desperate633

浅谈javascript中的的闭包作用域链引出闭包利用闭包突破作用域链的三种方法小结

闭包可以说是javascript中最令人迷惑的概念了。需要我们在实践中去慢慢理解,在实际编码中,由于闭包的效率和会产生大量无法销毁的内存,所以原则是尽量少使用闭...

731

扫码关注云+社区

领取腾讯云代金券