目录
③ 变量的作用域 (局部变量/内部变量[动态存储方式]、全局变量/外部变量[静态存储方式])
一、函数的概念和模块化程序设计
(1)C 语言函数的特点
- 一个 C 程序由一个或多个函数组成,其中必须有且只能有一个 main 函数(称为主函数)。
- C 程序的执行从主函数开始,如果在主函数中调用其他函数,调用后返回到主函数,并在主函数中结束整个程序的运行。
- 主函数可以调用其他函数,但其他函数不能调用主函数。主函数由操作系统调用,其他函数之间可以互相调用。
- 函数之间,没有从属关系,互相独立,不能嵌套定义。
(2)C 语言函数的分类
- 从函数定义的角度:分为库函数和用户函数。
- 从函数有无参数的角度:函数分为有参函数和无参函数。
- 从函数有无返回值的角度:函数分为有返回值函数和无返回值函数。
- 从函数作用范围的角度:函数分为外部函数和内部函数。
- C 语言允许将一个源程序分放在不同的程序文件中,采用分块编译、链接生成一个目标程序,其中每个程序文件称为一个 “编译单元” 。
- 每个编译单元可以包含若干个函数。
- 外部函数是可以被任何编译单元调用的。
- 内部函数只能在本编译单元被调用。
(3)C 语言函数的概念
函数:一个大的程序一般应分为若干个程序模块,每个模块实现一个特 定的功能,这些模块称为子程序,在 C 语言中子程序用函数实现。
(4)函数的一般格式
① 函数组成
任何函数(包括主函数)都是由 函数首部 和 函数体 两部分组成。
② 函数定义的一般形式
数据类型符 函数名(形式参数表){数据定义语句部分;执行语句部分;}
③ 函数首部
- “数据类型符” 规定本函数返回值的数据类型,可以是各种基本数据类型,也可以是指针类型(只要在函数名前面加一个 * )。需要注意的是,如果函数无返回值,数据类型符使用void。
- 需要注意的是,函数首部末尾不能加分号。
- “函数名” 是一个标识符,在同一个编译单元中的函数不能重名。
- “形式参数表” 是使用逗号分隔的若干个形式参数及其数据类型的说明,具体格式:
- 数据类型符 形式参数1[,数据类型符 形式参数2,…]
④ 函数体
- “数据定义语句部分” 由若干个数据定义语句组成,用来定义本函数中使用到的变量、数组和指针变量等。
- “执行语句部分” 由本函数中完成函数功能的程序段组成。如果是有返回值的函数,则其中至少有一条返回语句 “return(表达式);” ,表达式的值就是本函数的返回值。返回语句中的圆括号可以省略。函数的返回值是通过函数中的返回语句获得的。返回语句的一般格式是 return(返回值表达式); 如果是无返回值的函数,则返回语句应为 “return;” ,也可以省略返回语句。返回语句在函数体的执行语句部分的位置是任意的。返回语句的功能是结束本函数的运行,返回到主调函数的函数调用语句继续执行。
⑤ 说明
C 语言中,所有函数(包括主函数)都是 平行 的。 一个函数的定义,可以放在程序中的任意位置,主函数之前或之后 。但是在一个函数的函数体内不能再定义另一个函数,即不允许函数的嵌套定义。
(5)各类函数定义的格式
① 无参函数
无参函数的定义:如果一个函数在被调用时不需要主调函数向其传递数据,则该函数可以定义为无参函数
无参函数定义的一般格式:
数据类型符或 void 函数名(void){数据定义语句部分;执行语句部分;}void 表示函数没有参数,void 也可以省略,而只在函数名后面,跟一对空括号,但这对空括号不能省略。
无参函数定义的一般形式:
② 有参函数
有参函数的定义:如果一个函数在被调用时需要主调函数向其传递数据,则该函数可以定义为有参函数
有参函数定义的一般格式:
数据类型符或void 函数名(形式参数表){数据定义语句部分;执行语句部分;}
有参函数定义的一般形式:
空函数:为扩充功能预留,在主调函数中先占一个位置。
对形参的声明的传统方式:即把对形参的声明放在函数定义的下一行
③ 无返回值函数
无返回值函数定义的一般格式:
void 函数名(形式参数表或void){数据定义语句部分;执行语句部分;}
④ 有返回值函数
有返回值函数定义的一般格式:
数据类型符 函数名(形式参数表或void){数据定义语句部分;执行语句部分;}
⑤ 有返回值函数和无返回值函数的区别
主要区别体现在两个方面:
❶ 数据类型符:
- 有返回值函数的数据类型符可以选取任意的数据类型符
- 无返回值函数的数据类型符只能是 void
❷ 函数体:
- 有返回值函数的函数体中必须有语句 return(表达式);
- 无返回值函数的函数体中可以有也可以省略语句 return;
(6)返回值类型 / 函数类型
返回值类型:
- 函数返回值的类型即函数类型,是指返回给主调函数的结果的类型,应根据具体函数的功能确定。
- 如果函数不返回任何值,则函数返回值类型定义为 void,称为 “无类型” 。
(7)函数调用的一般格式
函数调用的一般形式:函数名([实际参数表]);
- 实参的个数、类型和顺序,应该与被调函数所要求的形参个数、类型和顺序一致,才能正确地进行函数之间的数据传递。
- 当有多个实参时,实参之间用逗号分隔。
- 无参函数没有参数,则调用时函数名后面跟一对空的圆括号,但这对圆括号必须保留。
(8)函数的形式参数和实际参数
- 形参是在函数定义时设置的,用来接收从主调函数传来的对应的实参数据。
- 实参是调用函数时的实际参数,实参可以是常量、变量或表达式,也可以是函数的返回值,无论哪种形式必须有确定的值。
- 实参在数量、类型和顺序上与形参必须一一对应和匹配。
形式参数:定义函数时函数名后面括号中的变量名
实际参数:调用函数时函数名后面括号中的表达式
说明:
- 实参可以是常量、变量或表达式。必须有确定的值。当函数调用时,将实参的值传递给形参,若是数组名,则传送的是数组首地址
- 形参必须指定类型,只能是简单变量或数组,不能是常量或表达式
- 形参与实参类型一致,个数相同顺序相同
- 若形参与实参类型不一致,自动按形参类型转换 —— 函数调用转换
- 形参在函数被调用前不占内存;函数调用时为形参分配内存;调用结束,内存释放
- 实参对形参的数据传送是值传送,也是单向传送,当被调函数的形参发生变化时,并不改变主调函数实参的值。形、实参占据的是不同的存储单元
(9)函数的返回
① 返回语句的使用方式
- 无返回值的函数 return;
- 有返回值的函数 return(返回值表达式); 或 return 返回值表达式;
- 函数的返回值是通过被调函数中的返回语句获得的。
- 若函数没有返回值,则应将函数返回值的类型定义为 void,在函数体中可以没有返回语句,也可以使用无返回值的 return 语句,但是不能使用有返回值的 return 语句。
- 若函数有返回值,则应将函数定义为非 void 类型,在函数体中只能使用有返回值的 return 语句,而且必须有该语句。
- 函数的返回值只能有一个,但可以有多个 return 语句,一旦执行到其中一个 return 语句,则立即返回主调函数,被调函数中的其他语句不再执行。
- return 语句中表达式的数据类型应与函数首部定义的函数返回值的类型一致。
② 函数的返回值
返回语句形式:
- return(表达式);
- return 表达式;
功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数
说明:1. 函数的返回值,必须用 return 语句带回。2. return 语句只能把一个返值传递给调用函数。3. 函数中可有多个 return 语句,执行哪一个由程序执行情况来定。
- 如:if(a>b) return(a);
- 如:else return(b);
4. return 后的值可以是一个表达式
- 如:return(x > y ? x : y);
5. 返回值的类型为定义的函数类型,不指定的按整型处理。
- 如:int max(int x, int y)
- 如:float min(float a,float b)
- 如:double abc(float d1,float d2)
二、函数声明
函数声明的一般形式:extern 数据类型符 函数名(形参表);
- 需要注意的是,函数声明是一条语句,末尾加分号;而函数首部不是语句,末尾不加分号。
说明:
- 函数声明告知编译程序函数返回的数据类型、函数所要接收的参数个数、类型和顺序,编译程序利用函数声明校验函数调用是否正确。
- 函数声明中可以只说明形参类型和形参个数,而无须说明形参名。
- 函数声明可以在主调函数中,也可以在所有函数的外部。
- 函数声明、函数定义、函数调用要保持一致。
- 如果程序中没有包含函数声明,则编译程序会用第一次出现时的该函数来构造函数声明。
三、函数的参数和数据传递方式
C 语言规定在函数之间传递数据包括 值传递 、 地址传递 、 返回值 和 全局变量传递 四种方式。
(1)值传递方式
- 值传递方式是在形参和实参之间传递数据的一种方式,值传递方式传递的是参数值。
- 判断是否是值传递方式的唯一方法是看函数定义时的形参是不是变量形式:如果形参是变量,则是值传递方式。
- 值传递方式能够确保不管在被调函数中怎样操作或改变形参的内容,但主调函数中的实参并为发生变化。
- 实参对形参的数据传递是单向的,即只能从主调函数将实参值传递给被调函数的形参,而不能把被调函数的形参值反向传递给主调函数的实参。
① 数组元素作为函数实参
- 数组元素只能用做函数实参,和变量作为函数实参一样。
- 在调用函数时,把数组元素的值传递给形参,实现单向的值传递方式。
(2)地址传递方式
① 数组名作为函数参数
- 数组名作为函数参数时,既可以作为形参,也可以作为实参。
- 数组名作为函数参数时,要求形参和相应的实参必须是同类型的数组,都必须有明确的数组定义。
- 如果形参是数组名,则传递方式称为 “地址传递方式” 。
- 数组名作为形参时,实参向形参传递的是实参数组的首地址,实参数组和形参数组的各元素按照存储结构一一对应共享存储空间。
数组名作函数参数时,实参和形参 两个数组共占同一段内存单元,形 参数组的元素值改变会使实参数组 元素的值同时变化。
(3)返回值传递方式
利用返回值的数据传递方式并不是在形参和实参之间进行数据传递,而是通过函数调用直接返回一个值到主调函数。
利用返回值传递数据,在定义函数时需要注意以下两点:
- 函数首部中需要有 “数据类型符” ,说明该该函数返回值的数据类型。
- 函数体中需要有语句 “return(表达式);” ,其中的表达式即是函数的返回值。
(4)全局变量传递方式
① 变量的存储类型(自动型、寄存器型、外部型、静态型)
C 语言中变量的存储类型分为四种:
- 自动型(auto)
- 寄存器型(register)
- 外部型(extern)
- 静态型(static)
② 变量的存储方式(静态存储方式、动态存储方式)
C 语言中变量的存储方式分为两大类:
- 静态存储方式:外部型变量、静态型变量
- 动态存储方式:自动型变量、寄存器型变量
③ 变量的作用域 (局部变量/内部变量[动态存储方式]、全局变量/外部变量[静态存储方式])
变量按其作用域,可分为:
- 局部变量(内部变量)
- 全局变量(外部变量)
局部变量和全局变量:
- 生存期覆盖了定义点到整个程序结束的变量称为 “全局变量”;生存期只覆盖了某个函数(或复合语句)的变量称为 “局部变量” 。
- 全局变量存放在静态存储区,属于静态存储方式。
- 全局变量的生存期与程序运行期相同。
- 定义全局变量时如果没有初始化,则自动赋值 0(整型和实型)或 ‘\0’(字符型)。
- 用 extern 在一个文件内扩展全局变量的作用域。全局变量的作用域是从它的定义处开
- 始到本程序文件末尾。
- 用 extern 将全局变量的作用域扩展到其他文件。
- 用 static 将全局变量的作用域限制在本文件。
- 对于局部变量而言,声明存储类型的作用是指定变量的存储位置(静态存储区或动态存储区)和生存期;对于全局变量而言,声明存储类型的作用是扩展或限制变量的作用域。
④ 自动型变量 [动态存储方式]
【格式】 [auto] 数据类型 变量表;
- 关键字 auto 可以省略。
- 当省略 auto 时,C 语言默认 auto 型,即定义变量时不特别声明存储类型的都默认自动型变量。
【说明】
- 自动型变量存放在内存的动态存储区,属于动态存储方式。从分配到释放之间是自动型变量的生存期。
- 定义自动型变量时如果没有初始化,则其值是不确定的,是系统赋予的随机数;如果初始化,则赋初值操作是在进入所定义的函数或复合语句时进行的,且每次都要重新初始化。
- 由于自动型变量的作用域和生存期都局限于定义它的个体内,因此不同的个体中允许使用重名的变量而不会混淆。
⑤ 静态型变量 [静态存储方式]
【格式】 static 数据类型 变量表;【说明】
- 静态型变量存放在内存的静态存储区,属于静态存储方式。静态变量的的生存期与程序的运行期相同。因为函数中的静态变量在函数调用结束时不释放占用的存储空间,因此其值能保留下来,供下一次调用该函数时使用。
- 定义静态型变量时如果没有初始化,则编译系统自动赋值0(整型和实型)或 ‘\0’(字符型)。
- 静态型变量如果初始化,只有第一次执行定义语句时随着分配内存赋予初值,且只赋一次初值。
- 静态型变量在程序运行期间始终存在,但在其作用域以外不能使用。
⑥ 寄存器型变量 [动态存储方式]
【格式】 register 数据类型 变量表;【说明】
- 只有局部变量才能定义为寄存器型变量,全局变量不行。
- 对于占用字节数较多的变量,如long、float和double型的变量一般不能定义为寄存器型变量。
- 对寄存器型变量的实际处理随系统而异。
- 由于CPU具有的通用寄存器数量有限,所以允许定义的寄存器型变量的数量也有限,不能定义任意多个寄存器型变量,具体限制取决于运行环境和编译系统。
⑦ 外部型变量 [静态存储方式]
【格式】 extern 数据类型 变量表;【说明】 外部型变量是专门用于在多个编译单元之间传递数据的。
⑧ 局部变量 / 内部变量
局部变量:【定义】在 函数内定义 , 只在本函数内有效【说明】
- main 中定义的变量只在 main 中有效
- 不同函数中同名变量,占不同内存单元
- 形参属于局部变量
- 可定义在复合语句中有效的变量
- 局部变量可用存储类型:auto register static (默认为auto)
内部变量:【定义】在一个函数(或复合语句)内部定义的变量称为 “内部变量” 。【注意】关于内部变量的作用域:
- 主函数中定义的内部变量,只能在主函数中使用,其他函数不能使用。
- 形参变量也是内部变量,仅限于函数内使用。
- 允许在不同函数中使用重名的变量,它们代表不同的对象,分配不同的内存单元,互不干扰,也不会发生混淆。
- 符合语句中也可以定义变量,所定义的变量是内部变量,其作用域只在该复合语句范围内。
⑨ 全局变量 / 外部变量
【定义】在 函数外定义 ,可为 本文件所有 函数共用, 也叫外部变量。【 有效范围 】 从 定义变量的位置开始 到本源文件结束,及有 extern 说明 的其它源文 件【说明】全局变量的使用, 增加了函数间数据联 系的渠道 , 同一文件中的所有函数都能 引用全局变量的值 , 当某函数改变了全 局变量的值时 , 便会影响其它的函数。
外部变量:【定义】 在函数外部定义的的变量称为 “外部变量” 。外部变量不属于任何一个函数,其 作用域是从定义外部变量的位置开始到本源程序文件结束。【注意】关于外部变量的作用域:
- 在同一源程序文件中,允许外部变量和内部变量重名。
- 外部变量的作用域是从定义点到本文件结束。
内部变量和外部变量的使用说明:
- 外部变量只能定义为无存储类型或静态型(static),不能定义为自动型(auto)或寄存器型(register)。 内部变量可以定义为自动型和寄存器型和静态型。
- 外部变量都是全局变量,在整个程序运行期间不释放所分配的内存空间,其作用域是从定义点带源程序结束,即在定义后的任何一个函数中均可使用。
- 声明为 auto 或 register 类型的内部变量是局部变量,只在所定义的函数或复合语句中存活,其作用域是所定义的函数或复合语句,一旦离开了所定义的函数或复合语句则不能在再使用,如果再次进入所定义的函数或复合语句,则需重新定义后再使用。
- 声明为 static 存储类型的内部变量是局部变量,在整个程序运行期间都不释放。其作用域只是所定义的函数或复合语句,一旦离开所定义的函数或复合语句则不能再使用,但该变量仍在生存期,所分配的内存并不释放,仍保留退出时的值。
- 外部变量是不能重名的。
⑩ 全局变量数据传递方式
利用全局变量的数据 传 递 方 式:利用全局变量传递数据的方式并不是在形参和实参之间传递数据,而是利用在主调函数和被调函数都有效的全局变量,在主调函数和被调函数之共享数据。
全局变量有两种:
- 在任何函数之外定义的全局变量,其作用域覆盖了定义点到整个源程序结束的所有函数,这种全局变量叫作“外部变量”;
- 在函数体内部声明为 static 型的变量,该变量从函数返回后,仍保留所分配的内存,但是不能使用,其作用域仍是该函数体内。这种全局变量叫作 “内部变量” 。
四、函数的嵌套调用和递归调用
C 语言允许函数的嵌套调用和递归调用。
(1)函数的嵌套调用
函数的嵌套调用是指在调用函数时,被调函数又调用了其他函数或自身。当被调函数是函数自身时,称为函数的递归调用,递归调用是嵌套调用的一种特例。
(2)函数的递归调用
在调用一个函数的过程中又直接或间接地调用其自身,称为递归调用。递归调用又称为自调用函数。
五、常用库函数
1. 计算整型绝对值函数
【函数首部】int abs(int x)【返回值】返回整数 x 的绝对值
2. 计算长整型绝对值函数
【函数首部】long labs(long x)【返回值】返回长整型数 x 的绝对值
3. 计算实型绝对值函数
【函数首部】double fabs(double x)【返回值】返回双精度实数 x 的绝对值
4. 计算小于或等于x的最大整数函数
【函数首部】double floor(double x)【返回值】返回小于或等于 x 的最大整数对应的双精度实数
5. 计算大于或等于x的最小整数函数
【函数首部】double ceil(double x)【返回值】返回大于或等于 x 的最小整数对应的双精度实数
6. 计算平方根函数
【函数首部】double sqrt(double x)【返回值】返回 x 的平方根【说明】x 的值 ≥ 0
7. 计算常用对数函数
【函数首部】double log10(double x)【返回值】返回常用对数 log 以 10 为底 (x) 的值【说明】x 的值 > 0
8. 计算自然对数函数
【函数首部】double log(double x)【返回值】返回自然对数 ln(x) 的值【说明】x 的值 > 0
9. 计算指数函数
【函数首部】double exp(double x)【返回值】返回 e 的 x 次幂的值
10. 计算10的x次幂函数
【函数首部】double pow10(int x)【返回值】返回 10 的 x 次幂的值
11. 计算x的y次方函数
【函数首部】double pow(double x,double y)【返回值】返回 x 的 y 次方的值【说明】不能出现 x、y 均 < 0;或 x ≤ 0,而 y 不是整数的情况
12. 计算正弦函数
【函数首部】double sin(double x)【返回值】返回正弦函数 sin(x) 的值【说明】x 以弧度为单位。如果是角度,则用 x*3.1415926/180 转换为弧度
13. 计算余弦函数
【函数首部】double cos(double x)【返回值】返回正弦函数 cos(x) 的值【说明】x 以弧度为单位。如果是角度,则用 x*3.1415926/180 转换为弧度
14. 计算正切函数
【函数首部】double tan(double x)【返回值】返回正弦函数 tan(x) 的值【说明】x 以弧度为单位。如果是角度,则用 x*3.1415926/180 转换为弧度
15. 字符串转换成浮点数函数
【函数首部】double atof(char *x)【返回值】返回 x 所指向的字符串转换成的实数【说明】x 所指向的字符串中存放的应当是一个实数形式
16. 字符串转换成整数函数
【函数首部】int atoi(char *x)【返回值】返回 x 所指向的字符串转换成的整型数【说明】x 所指向的字符串中存放的应当是一个整数形式
17. 判断是否字母函数
【函数首部】int isalpha(int x)【返回值】若 x 中存放的字符是字母。则返回非 0(真);否则,返回 0(假)
18. 判断是否小写字母函数
【函数首部】int islower(int x)【返回值】若 x 中存放的字符是小写字母。则返回非 0(真);否则,返回 0(假)
19. 判断是否大写字母函数
【函数首部】int isupper(int x)【返回值】若 x 中存放的字符是大写字母。则返回非 0(真);否则,返回 0(假)
20. 判断是否数字字符函数
【函数首部】int isdigit(int x)【返回值】若 x 中存放的字符是数字字符。则返回非 0(真);否则,返回 0 (假)
21. 将大写字母转换为小写字母函数
【函数首部】int tolower(int x)【返回值】若 x 中存放的字符是大写字母。则返回是对应的小写字母;若 x 中存放的字符不是写大字母。则返回等于 x 的原值。
22. 将小写字母转换为大写字母函数
【函数首部】int toupper(int x)【返回值】若 x 中存放的字符是小写字母。则返回是对应的大写字母;若 x 中存放的字符不是写小字母。则返回等于 x 的原值。