【C语言】C语言期末突击/考研--函数

目录

一、函数的声明与定义-嵌套调用

1.1.函数的声明与定义

1.2.函数的分类与调用

二、函数的递归调用

三、局部变量与全局变量

3.1.全局变量解析形参实参解析

3.2.局部变量与全局变量

四、练习题及解析


一、函数的声明与定义-嵌套调用

1.1.函数的声明与定义

函数间的调用关系是,由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意次。如图1.1 所示. 下面来看例1.1例中有两个c文件,func.c 是子函数printstar和print message的实现,也称定义;main.c 是main函数,func.h 中存放的是标准头文件的声明和main函数中调用的两个子函数的声明,如果不在头文件中对使用的函数进行声明,那么在编译时会出现警告。

【例1.1】函数嵌套调用:

cpp 复制代码
/*func.h*/
#include <stdio.h>
#include <stdlib.h>
int printstar(int i); //函数声明
void print_message();
cpp 复制代码
/*func.c*/
#include "func.h"
int printstar(inti) //i即为形式参数
{
    printf("********************\n");
    printf("printstar %d\n", i);
    return i + 3;
}
void print_message() //可以调用printstar
{
    printf("how do you do\n");
    printstar(3);
}
cpp 复制代码
/*
main.c
*/
#include "func.h"
int main()
{
    int a=10;
    a=printstar(a);
    print_message();
    printstar(a);
    return 0;
}

C语言的编译和执行具有以下特点:

(1) 一个C程序由一个或多个程序模块组成,每个程序模块作为一个源程序文件。对于较大的程序,通常将程序内容分别放在若干源文件中,再由若干源程序文件组成一个C程序。这样处理便于分别编写、分别编译,进而提高调试效率。一个源程序文件可以为多个C程序共用。

(2)一个源程序文件由一个或多个函数及其他有关内容(如命令行、数据定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位而不是以函数为单位进行编译的。main.c 和func.c分别单独编译,在链接成为可执行文件时,main中调用的函数printstar和print_message才会通过链接去找到函数定义的位置。

(3) C程序的执行是从main函数开始的,如果在main函数中调用其他函数,那么在调用后会返回到main函数中,在main函数中结束整个程序的运行

(4)所有函数都是平行的,即在定义函数时是分别进行的,并且是互相独立的。一个函数并不从属于另一函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是由系统调用的,例1.1的main函数中调用print message函数,而print message函数中又调用printstar 函数,我们把这种调用称为嵌套调用。

函数的声明与定义的差异如下:

(1)函数的定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一一个完整的、独立的函数单位。

(2) 函数的声明的作用是把函数的名字、函数类型及形参的类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。

**隐式声明:**C语言中有几种声明的类型名可以省略。例如,函数如果不显式地声明返回值的类型,那么它默认返回整型;使用旧风格声明函数的形式参数时,如果省略参数的类型,那么编译器默认它们为整型。然而,依赖隐式声明并不是好的习惯,因为隐式声明容易让代码的读者产生疑问:编写者是否是有意遗漏了类型名?还是不小心忘记了?显式声明能够清楚地表达意图!

1.2.函数的分类与调用

从用户角度来看,函数 分为如下两种:

(1) 标准函数:即库函数,这是由系统提供的,用户不必自己定义的函数,可以直接使用它们 ,如printf函数、scanf函数。不同的C系统提供的库函数的数量和功能会有一些不同,但许多基本的函数是相同的。

(2)用户自己定义的函数:用以解决用户的专门需要。

从函数的形式看 ,函数分为如下两类:

(1) 无参函数:一般用来执行指定的一组操作。在调用无参函数时,主调函数不向被调用函数传递数据。在例1.1 中,print_message就是无参函数。无参函数的定义形式如下:

类型标识符 函数名()

{

声明部分
语句部分

}

(2)有参函数:主调函数在调用被调用函数时,通过参数向被调用函数传递数据。有参函数的定义形式如下:

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

{

声明部分
语句部分

}

在例1.1中, printstar 就是有参函数,int i对应的i为形式参数,主调函数和被调用函数之间存在数据传递关系。

二、函数的递归调用

我们把函数自身调用自身的操作,称为递归函数, 递归函数一定要 有结束条件,否则会产
生死循环!

假设现在要求读者写一个程序来求数字n的阶乘。读者可能会觉得这很简单吗,写个for循环就可以实现,然而,使用递归来实现更好一些,因为使用递归在解决一些问题时,可以让问题变得简单,降低编程的难度.比如接下来的题目:假如有n个台阶,一次只能上比1个台阶或2个台阶,请问走到第n个台阶有几种走法?为便于读者理解题意,这里举例说明如下:假如有3个台阶,那么总计就有3种走法:第一种为每次上1个台阶,上3次;第二种为先上2个台阶,再上1个台阶;第三种为先上1个台阶,再上2个台阶。具体实现请看例2.1

【例2.1】n的阶乘的递归调用实现:

cpp 复制代码
#include <stdio.h>
//求n的阶乘
int f(int n)
{
    if(1==n)
    {
        return 1;
    }
    return n*f(n-1);
}

//走楼梯
int step(int n){
    if(1==n)
    {
    return 1;
    }
    if(2==n)
    {
    return 2;
    }
    return step(n- 1)+step(n- -2);
}
int main(){
    int n;
    int ret;
    scanf(" %d" ,&n); //请输入数字的大小
    ret=f(n);
    printf(" %d\n",ret);
    scanf("%d" ,&n); //请输入台阶数
    ret=step(n);
    printf("%d\n",ret);
    return 0;
}

三、局部变量与全局变量

3.1.全局变量解析形参实参解析

不同的函数之间传递数据时,可以使用的方法如下:

(1) 参数:通过形式参数和实际参数。
(2)返回值:用return语句返回计算结果。
(3)全局变量:外部变量。

下面来看一个全局变量的实例,如例3.1所示。

【例3.1】全局变量的使用:

cpp 复制代码
#include <stdio.h>
int i=10; //全局变量
void print(int a)
{
    printf("print i=%d\n",i);
}
int main()
{
    {
        int j=5;
    } //局部变量的有效范围是离自已最近的花括号
    printf( "main i=%d\n",i);
    i=5;
    print(i);
    return 0;
}

全局变量存储在哪里? 如图3.1所示,全局变量i存储在数据段,所以main函数和print函数都是可见的。全局变量不会因为某个函数执行结束而消失,在整个进程的执行过程中始终有效,因此工作中应尽量避免使用全局变量!在前面中,我们在函数内定义的变量都称为局部变量,局部变量存储在自己的函数对应的栈空间内,函数执行结束后,函数内的局部变量所分配的空间将会得到释放。如果局部变量与全局变量重名,那么将采取就近原则,即实际获取和修改的值是局部变量的值

练习:如果把print(int a)改为print(int i),那么print函数的打印结果会是多少?

关于形参与实参 的一些说明如下。

(1)定义函数中指定的形参,如果没有函数调用,那么它们并不占用内存中的存储单元。只

有在发生函数调用时,函数print中的形参才被分配内存单元。在调用结束后,形参所占的内存

单元也会被释放。

(2)实参可以是常量、变量或表达式,但要求它们有确定的值,例如,print(i+3)在 调用时

将实参的值i+3赋给形参。print 函数可以有两个形参,如print(int a,int b)

(3)在被定义的函数中,必须指定形参的类型。如果实参列表中包含多个实参,那么各参数

间用逗号隔开.实参与形参的个数应相等,类型应匹配,且实参与形参应按顺序对应,- - - 传递

数据.

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

(5)实参向形参的数据传递是单向" 值传递",只能由实参传给形参,而不能由形参传回给
实参。在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参

单元被释放,实参单元仍保留并维持原值.

(6)形参相当于局部变量,因此不能再定义局部变量与形参同名,否则会造成编译不通。

3.2.局部变量与全局变量

3.2.1. 内部变量

在一个函数内部定义的变量称为内部变量。它只在本函数范围内有效,即只有在本函数内才
能使用这些变量,故也称局部变量。

关于局部变量需要注意如下几点:

(1)主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序

中有效.主函数也不能使用其他函数中定义的变量。

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

(3)形式参数也是局部变量。

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

复合语句也称"分程序"或"程序块"。例3.1中的int j=5就是如此,只在离自己最近的花括号内有效,若离开花括号,则在其下面使用该变量会造成编译不通。

(5)注意一个细节,for 循环的小括号内定义的inti,在离开for循环后,是不可以再次使用

3.2.2.外部变量

函数之外定义的变量称为外部变量。外部变量可以为本文件中的其他函数共用,它的有效范
围是从定义变量的位置开始到本源文件结束,所以也称全程变量。

关于全局变量需要注意如下几点:

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

(2) 使用全局变量过多会降低程序的清晰性。在各个函数执行时都可能改变外部变量的值,程序容易出错,因此要有限制地使用全局变量(初试时尽量不用)。

(3)因为函数在执行时依赖于其所在的外部变量,如果将一个函数移到另一个文件中,那么还要将有关的外部变量及其值一起移过去。然而,如果该外部变量与其他文件的变量同名,那么就会出现问题,即会降低程序的可靠性和通用性. C语言一般要求把程序中的函数做成一个封闭体,除可以通过"实参-→形参"的渠道与外界发生联系外,没有其他渠道。

四、练习题及解析

1、程序的人口函数是main函数,因此每个项目中都必须 含有一个main函数

A正确 B错误

2、一个项目中可以有多个 .c文件,多个 .h 文件

A正确 B错误

3、在程序中,main 函数调用a函数,a函数调用b函数,这样的调用就是嵌套调用,b函

数执行完毕会返回a函数, a函数执行完毕再返回main函数

A正确 B错误

4、从函数的形式看,函数分为有参函数和无参函数,有参函数我们可以传递参数给函数,调用时

我们传递的参数叫实参,函数定义位置,小括号内的参数是形参

A正确 B错误

5、函数自身调用自身的操作,称为递归函数,递归函数一定 要有结束条件,否则会产生死

循环

A正确 B错误

6、能用递归实现的,循环也可以实现,但是递归编写降低了难度

A正确 B错误

7、我们只能 给递归函数传一一个函数,不能写两个形参

A正确 B错误

8、定义在函数外部的变量称为外部变量,也叫全局变量,是从定义位置开始,到文件结尾

一致有效

A正确 B错误

9、定义函数中指淀的形参,如果没有函数调用,那么它们会占用内存中的存储单元

A正确 B错误

10、实参与形参的个数应相等,类型应匹配,且实参与形参应按顺序对应

A正确 B错误

11、局部变量 在函数内有效

A正确 B错误

解析:

1、 A 解释:这个需要记住,程序从main函数开始执行,因此每个项目必须有main函数,当然只

能有一个main函数

**2、**A 解释:项目中采用多个c文件,可以起到分别编写、分别编译,进而提高调试效率,我们往往用一个c文件,是为了咱们自己学习方便,编写方便

**3、**A 解释:嵌套调用以及它的执行过程,我们要掌握

**4、**A 解释:我们往往使的都是有参函数,无参函数使用的比较少,但是对于它们的区别我们要清晰掌握

5、 A 解释:递归函数的定义要非常清楚,考研初试大题多次考过递归,一定要熟练掌握,同时自

已编写递归是要注意有结束条件

**6、**A 解释:我们通过递归来解决某个问题,是因为使用循环的难度不好解决

**7、**B 解释:递归函数可以有多个形参

**8、**A 解释:这个需要记住,很多人会认为全局变量是在整个代码文件有效,实际是不正确的

**9、**B 解释:定义函数中指定的形参,如果没有 函数调用,那么它们并不占用内存中的存储单,当发生调用时,才分配内存

**10、**A 解释:这个需要记住,很重要,考研大题一定涉 及到写子函数,同时是有参数的

**11、**B 解释:局部变量是离自己最近的大括号内有效,并不是在整个函数内有效

下一期:

【C语言】C语言期末突击/考研--结构体与C++引用

相关推荐
----云烟----15 分钟前
QT中QString类的各种使用
开发语言·qt
lsx20240619 分钟前
SQL SELECT 语句:基础与进阶应用
开发语言
开心工作室_kaic44 分钟前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it1 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康1 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神1 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
搬砖的小码农_Sky1 小时前
C语言:数组
c语言·数据结构
宅小海2 小时前
scala String
大数据·开发语言·scala
qq_327342732 小时前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍2 小时前
Scala的Array数组
开发语言·后端·scala