第四章 函数与程序结构

函数可以把大的计算任务分解成若干个较小的任务,程序设计人员可以基于函数进一步构造程序,而不需要重新编写一些代码。一个设计得当的函数可以把程序中不需要了解的具体操作细节隐藏起来,从而使整个程序结构更加清晰,并降低修改程序的难度。

C语言在设计中考虑了函数的高效性与易用性这两个因素。C语言程序一般都由许多小的函数组成,而不是由少量较大的函数组成。一个程序可以保存在一个或者多个源文件中。各个文件可以单独编译,并可以与库中已编译过的函数一起加载。

4.1 函数的基本知识

函数是一段组织好的、可重复使用的、具有特定功能的代码,它能提高程序的模块化和代码的重复利用率。函数的基本结构包括函数名、参数列表、返回类型和函数体。

函数的基本定义如下:

函数的参数可以是任何数据类型,参数的传递方式有值传递和地址传递两种。传值方式会复制参数的值传递给函数,传地址方式会传递参数的地址给函数,函数内部通过地址访问和修改参数。返回类型决定了函数返回值的类型。函数可以没有返回值,此时返回类型为void

函数的递归调用是函数自己调用自己,必须有一个明确的退出条件,否则会导致无限递归。例如,计算阶乘:

返回值为空的函数(即返回类型为void的函数)在C语言中有多种作用和用途‌。

首先,返回值为空的函数主要用于执行某些操作而不需要返回结果的情况。这些函数通常用于执行特定的任务,如打印消息、执行清理工作(如释放内存或关闭文件)等,因为它们不需要返回任何值给调用者‌。例如,一个打印消息的函数不需要返回任何数据,其返回类型为void。

其次,返回值为空的函数可以简化代码和减少资源占用。通过返回空值,可以避免在内存中创建和维护不必要的对象,从而减少资源占用和管理成本‌。

此外,返回值为空的函数在错误处理中也有应用。例如,在C++中,当函数无法执行其预期的任务、调用不正确、遇到错误或无法分配内存时,可以使用空值返回值来表示函数没有提供有意义的结果。调用者需要检查返回值并妥善处理这种情况‌。

4.2 返回非整形值的函数

在C语言中,返回非整形值的函数是很常见的。例如,你可能会写一个函数,它返回一个浮点数或者一个字符。

以下是一些返回非整形值的函数的例子:

  1. 返回浮点数的函数:

4.3 外部变量

C语言程序可以看成由一系列外部对象构成,这些外部对象可能是变量或函数。形容词external与internal是相对的,internal用于描述定义在函数内部的函数参数及变量。外部变量定义在函数之外,因此可以在许多函数中使用。由于C语言不允许在一个函数中定义其他函数,因此函数本身是“外部的”。默认情况下,外部变量与函数具有下列性质:通过同一个名字引用的所有外部变量(即使这种引用来自单独编译的不同函数)实际上都是引用同一个对象(标准中把这一性质称为处部链接)。在这个意义上,外部变量类似于Fortran语言的COMMON块或Parcal语言中在最外层程序块中声明的变量。

如果函数之间需要共享大量的变量,使用外部变量要比使用一个很长的参数表更方便有效。但是,这样做必须非常谨慎,因为这种方式可能对程序结构产生不良的影响,而且可能会导致程序中各个函数之间具有太多的数据联系。
外部变量的用途还表现在它们与内部变量相比具有更大的作用域和更长的生存期。自动变量只能在函数内部使用,从其所在的函数被调用时变量开始存在,在函数退出时变量也将消失。而外部变量是永久存在的,它们的值在一次函数调用到下一次函数调用之间保持不变。因此,如果两个函数必须共享某些数据,而这两个函数互不调用对方,这种情况下最方便的方式便是把这些共享数据定义为外部变量,而不是作为函数参数传递。

4.4 作用域规则

名字的作用域指的是程序中可以使用该名字的部分。对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数的参数也是这样的,实际上可以将它看作是局部变量。外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。

如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern。将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此以外还将引起存储器的分配。

在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它(定义外部变量的源文件中也可以包含对该外部变量的extern声明)。外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度。外部变量的初始化只能出现在其定义中。

4.5 头文件

源程序分成不同的源文件的主要原因是为了提高代码的组织性、可读性和可维护性‌。随着软件规模和复杂度的增加,源代码的行数也会增多,如果所有的代码都写在一个文件中,会导致文件过于庞大,阅读和修改都会变得困难。因此,将代码分成多个源文件,每个文件负责不同的功能模块或特定的任务,可以显著提高开发效率、代码的可读性和可维护性‌。

例如,在一个大型项目中,可以将用户界面相关的代码放在一个文件中,业务逻辑相关的代码放在另一个文件中,数据访问相关的代码放在第三个文件中。这样,不同的开发人员可以并行工作,分别负责不同的模块,从而提高开发效率‌。

头文件(.h)主要用于声明函数和全局变量,以便在多个源文件(.c)中复用。头文件可以防止多重包含,也就是说,一个头文件可以被多个源文件包含而不会出现重定义错误。

4.6 静态变量

static关键字可以用来修饰局部变量、全局变量和函数。以下是static关键字在C语言程序中的一些应用和解决方案。

1.修饰局部变量

当static修饰局部变量时,该变量被称为静态局部变量。它的生命周期变为整个程序的生命周期,但是其作用域仍然仅限于其所在的函数或者代码块。

2.修饰全局变量

当static修饰全局变量时,该变量的作用域仅限于声明它的文件,其他文件不能访问它。这被称为文件级作用域。

3.修饰函数

当static修饰函数时,该函数的作用域仅限于声明它的文件,其他文件不能访问它。这被称为文件级作用域。

4.7 寄存器变量

使用寄存器变量是一种优化手段,它允许你请求编译器将一个变量保持在CPU寄存器中,而不是在内存中。这样可以加快访问速度,但是会增加程序的复杂性,并可能导致更大的内存需求。

4.8 初始化

在不进行显式初始化的情况下,处部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义(即初值为无用的信息)。对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次。对于自动变量与寄存器变量来说,初始化表达式可以不是常量表达式:表达式中可以包含任意在此表达式之前已经定义的值,包括函数调用。

4.9 递归

递归是一种非常常见且有效的编程技巧。递归函数是一个直接或间接调用自身的函数。使用递归的关键是确定终止条件和递归步。例如计算阶乘:

4.10 C预处理器

C语言通过预处理器提供了一些语言功能。从概念上讲,预处理器是编译过程中单独执行的第一个步骤。两个最常用的预处理器指令是#include指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用于以任意字符序列替代一个标记)。以下是一些常见的C语言预处理器指令:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值