函数的定义、说明与调用 1函数的定义、说明与调用 (一)无参函数的定义形式 类型标识符 函数名() 函数体(包括声明部分和执行部分) 例: printf(“*******n”); (二)有参函数定义的一般形 型标识符 函数名(形式参数表) 函数体(包括声明部分和执行部分) (三)一个函数可以是空函数 如 dummy(){} 要正确实现函数间的相互调用需满足下列条件 第一,被调用函数必须存在且允许调用
函数的定义、说明与调用 4 . 1 函数的定义、说明与调用 (一)无参函数的定义形式 类型标识符 函数名( ) { 函数体(包括声明部分和执行部分) } 例:printstar( ) { printf( “ ********** \n”); } (二)有参函数定义的一般形式 类型标识符 函数名(形式参数表) { 函数体(包括声明部分和执行部分) } (三)一个函数可以是空函数 如 dummy( ) { } 要正确实现函数间的相互调用需满足下列条件: 第一,被调用函数必须存在且允许调用;
第二,必须给出满足函数运行时要求的参数 第三,在调用一个函数之前一般应该对被调用函数进行声明 (四)函数声明的一般形式 函数类型函数名(参数类型1,参数类型2,…,参数类型n): 或:函数类型函数名(参数类型1参数名1,参数类型2…) 如: int print( char format,…) fun(in har p1) float add (float, float) 注意:函数声明与函数定义不同! (五)函数调用的一般方法 函数调用形式:函数名(实参表列) 1.函数语句 如:前述例子中的 print0; 2.函数表达式 c=max(a, b) 3.函数参数 m=max(a, max(b, c)) printf(“%d”,max(a,b))
第二,必须给出满足函数运行时要求的参数; 第三,在调用一个函数之前一般应该对被调用函数进行声明。 (四)函数声明的一般形式 函数类型 函数名(参数类型 1,参数类型 2, …, 参数类型 n); 或:函数类型 函数名(参数类型 1 参数名 1,参数类型 2 …); 如:int print(char format, …); char fun2(int i, char p1); float add(float , float); 注意:函数声明与函数定义不同! (五)函数调用的一般方法 函数调用形式: 函数名(实参表列); 1.函数语句 如:前述例子中的 print(); 2.函数表达式 如:c=max(a,b); 3.函数参数 如:m=max(a, max(b,c)); printf(“%d”, max(a,b));
变量的存储类、作用域和存在性,内部和外部函数 变量的存储类、作用域和存在性,内部和外部函数 、局部变量 在一个函数内部定义的变量是内部变量,它只在本函数范围内有效。 二、全局变量 在函数之外定义的变量称为外部变量,即全局变量(全程变量)。 全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件 结束 使用全局变量可以增加函数间的数据联系 在同一源文件中,如果外部变量与局部变量同名,则在局部变量的作用范围内外部变 量不起作用 变量的存储类别 从变量的作用域(空间)来分,为全局和局部变量 从变量值存在的(生存期)时间来分,可以分为 1.动态存储方式与静态存储方式 静态存储方式:指在程序运行期间分配固定的存储空间的方式。 动态存储方式:在程序运行期间根据需要进行动态的分配存储空间 的方式 2.auto变量 调用函数时系统自动分配存储空间,在函数调用结束时自动释放这 存储空间,称这类局部变量为自动变量 自动变量用关键字auto作存储类别的声明。它也可省 3.用 static声明局部变量
变量的存储类、作用域和存在性,内部和外部函数 4 . 2 变量的存储类、作用域和存在性,内部和外部函数 一、局部变量 在一个函数内部定义的变量是内部变量,它只在本函数范围内有效。 二、全局变量 在函数之外定义的变量称为外部变量,即全局变量(全程变量)。 全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件 结束。 使用全局变量可以增加函数间的数据联系; 在同一源文件中,如果外部变量与局部变量同名,则在局部变量的作用范围内外部变 量不起作用; 变量的存储类别 从变量的作用域(空间)来分,为全局和局部变量。 从变量值存在的(生存期)时间来分,可以分为: 1. 动态存储方式与静态存储方式 静态存储方式:指在程序运行期间分配固定的存储空间的方式。 动态存储方式:在程序运行期间根据需要进行动态的分配存储空间 的方式。 2. auto 变量 调用函数时系统自动分配存储空间,在函数调用结束时自动释放这 些存储空间,称这类局部变量为自动变量。 自动变量用关键字 auto 作存储类别的声明。它也可省。 3. 用 static 声明局部变量
函数中变量的值在函数调用结束后不消失而保留原值,在下次该函 数调用时,该变量已有值,即为上一次函数调用结束时的值。该局部变量为静态局部变量。 main for(i=0;i<3;i++) printf(“%d”,f(a) 运行结果为: 说明 、静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期都不释放 自动变量属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释 、对静态局部变量在编译时赋初值,程序运行时,它已有初值,以后每次调用函数时不再重新赋初值 只是保留上次函数调用结束时的值 对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给 初值 对静态局部变量来说,如不赋初值,编译时自动赋初值0或空字符。 对自动变量来说,如不赋初值,它的值是一个不确定的值。 4、虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用。 4. register变量 将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存 中去存取。这种变量称为寄存器变量。 说明 只有局部自动变量和形式参数可作为 register变量 2、不能定义任意多个寄存器变 3、局部静态变量不能定义为寄存器变量。 5、用 extern声明外部变量 1、在一个文件内声明外部变量
函数中变量的值在函数调用结束后不消失而保留原值,在下次该函 数调用时,该变量已有值,即为上一次函数调用结束时的值。该局部变量为静态局部变量。 main() { int a=2, i; for( i=0; i<3; i++) printf(“%d”,f(a)); } 运行结果为:____________ 说明: 1、静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期都不释放。 自动变量属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释 放。 2、对静态局部变量在编译时赋初值,程序运行时,它已有初值,以后每次调用函数时不再重新赋初值 而只是保留上次函数调用结束时的值。 对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次 初值。 3、对静态局部变量来说,如不赋初值,编译时自动赋初值 0 或空字符。 对自动变量来说,如不赋初值,它的值是一个不确定的值。 4、虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用。 4. register 变量 将局部变量的值放在 CPU 中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存 中去存取。这种变量称为寄存器变量。 说明: 1、只有局部自动变量和形式参数可作为 register 变量。 2、不能定义任意多个寄存器变量。 3、局部静态变量不能定义为寄存器变量。 5、用 extern 声明外部变量 1、在一个文件内声明外部变量
na ln i extern A /*外部变量声明*/ nt A=13 /*定义外部变量*/ 2、在多文件的程序中声明外部变量 用 static声明外部变量 在定义外部变量时,加一个 static声明,可以使此变量只能用于本文件中 注:对外部变量加或不加 static声明,都是静态存储,只是作用范围不同,都是在编译时分 配内存的。 用 static来声明一个变量的作用有二: 对局部变量用 static声明,则为该变量分配的空间在整个程序执行期间始终存在。 全局变量用 static声明,则该变量的作用域只限于本文件模块。 另注:用auto、 register、 static声明变量时,是在定义变量的基础上加这些关键字,而不 单独使用。 /*定义整型变量a*/ static /*对变量a声明为静态变量* 此用法不对。编译时会被认为“重新定义” 7.关于变量的声明和定义 般讲,把建立存储空间的声明称定义,而把不需要建立存储空间的声明称为声明。 /*是声明不是定义* /*是定义*/
例:main() { extern A; /*外部变量声明*/ printf(“%d”,A); } int A=13; /*定义外部变量*/ 2、在多文件的程序中声明外部变量 6. 用 static 声明外部变量 在定义外部变量时,加一个 static 声明,可以使此变量只能用于本文件中。 注:对外部变量加或不加 static 声明,都是静态存储,只是作用范围不同,都是在编译时分 配内存的。 用 static 来声明一个变量的作用有二: 1、对局部变量用 static 声明,则为该变量分配的空间在整个程序执行期间始终存在。 2、全局变量用 static 声明,则该变量的作用域只限于本文件模块。 另注:用 auto、register、static 声明变量时,是在定义变量的基础上加这些关键字,而不 能单独使用。 如:int a; /*定义整型变量 a*/ static a; /*对变量 a 声明为静态变量*/ 此用法不对。编译时会被认为“重新定义”。 7. 关于变量的声明和定义 一般讲,把建立存储空间的声明称定义,而把不需要建立存储空间的声明称为声明。 例: main() { extern A; /*是声明不是定义*/ … } nt A; /*是定义*/ 注:
外部变量的定义只能一次,它在所有函数之外,而同一文件中的外部变量的声明可以有多次,它可 在函数内也可在函数外 对外部变量的初始化只能在定义时进行,而不能在声明中进行。 存储类别小结 作用域角度分 局部变量 自动变量即动态局部变量(离开函数,值就消失) 静态局部变量(离开函数,值仍保留) 寄存器变量(离开函数,值就消失) (形式参数可以定义为自动变量或寄存器变量 全局变量: 静态外部变量(只限本文件引用) 外部变量(即非静态的,允许其他文件引用) 从变量存在的时间分: 态存储:自动变量、寄存器变量、形式参数 静态存储:静态局部变量、静态外部变量、外部变量 从变量值存放的位置分 内存中的静态存储区:静态局部变量、静态外部变量、外部变量 内存中的动态存储区:自动变量和形式参数 寄存器:寄存器变量 static对局部变量和全局变量的作用不同 对局部变量来说,它使变量静态存储, 对全局变量来说,它使变量局部化(本文件),但仍为静态存储。 从作用域角度看,凡有 static声明的,其作用域是局限的,或局限于本函数内,或局限于本文 内 内部函数和外部函数 、内部函数
1、外部变量的定义只能一次,它在所有函数之外,而同一文件中的外部变量的声明可以有多次,它可 在函数内也可在函数外。 2、对外部变量的初始化只能在定义时进行,而不能在声明中进行。 8. 存储类别小结 从作用域角度分: 局部变量: 自动变量即动态局部变量(离开函数,值就消失) 静态局部变量(离开函数,值仍保留) 寄存器变量(离开函数,值就消失) (形式参数可以定义为自动变量或寄存器变量) 全局变量: 静态外部变量(只限本文件引用) 外部变量(即非静态的,允许其他文件引用) 从变量存在的时间分: 动态存储:自动变量、寄存器变量、形式参数 静态存储:静态局部变量、静态外部变量、外部变量 从变量值存放的位置分: 内存中的静态存储区:静态局部变量、静态外部变量、外部变量 内存中的动态存储区:自动变量和形式参数 寄存器:寄存器变量 static 对局部变量和全局变量的作用不同。 1. 对局部变量来说,它使变量静态存储, 对全局变量来说,它使变量局部化(本文件),但仍为静态存储。 2. 从作用域角度看,凡有 static 声明的,其作用域是局限的,或局限于本函数内,或局限于本文 件内。 内部函数和外部函数 一、内部函数
个函数只能被本文件中其他函数所调用。 即: static类型标识符函数名(形参表) 如: static int a, int b) 、外部函数 个函数可供其他文件调用,称为外部函数 定义时声明或调用时声明,即 extern fun (int a, int b) 定义时 o extern fun (int a, int b) 调用时 在定义时,也可省写 extern,即为外部函数 编译预处理 3编译预处理 宏定义 宏:代表一个字符串的标识符。 宏名:被定义为“宏”的标识符 宏代换(展开):在编译预处理时,对程序中所有出现的“宏名”,用宏定义中的字符串去代 的过程 不带参数的宏定义 般形式 标识符 字符串 说明:
一个函数只能被本文件中其他函数所调用。 即:static 类型标识符 函数名(形参表) 如:static int fun( int a,int b) 二、外部函数 一个函数可供其他文件调用,称为外部函数 定义时声明或调用时声明,即: extern int fun(int a, int b ) 定义时 或 extern fun(int a, int b ) 调用时 在定义时,也可省写 extern ,即为外部函数 编译预处理 4 . 3 编译预处理 1. 宏定义 宏: 代表一个字符串的标识符。 宏名:被定义为“宏”的标识符。 宏代换(展开):在编译预处理时,对程序中所有出现的 “宏名”,用宏定义中的字符串去代 换的过程。 一、不带参数的宏定义 一般形式: #define 标识符 字符串 说明:
1)宏名一般用大写表示,以便与变量名区分 使用宏名使程序易读,易修改。 (3)只作简单的置换,不作正确性检查 4)宏定义不是C语句,不必在行末加分号 5宏名的作用域一般从自定义命令到本源文件结束 6)可以用# undef命令终止宏定义的作用域 7)宏定义允许嵌套,允许层层置换。 8)宏名在源程序中用双引号括起来,则TC中预处理不对其作宏代换 ⑨)宏定义与定义的变量不同,宏只作字符替换,不分配内存空间 00对“输出格式”进行宏定义,可以减少书写麻烦 、带参数的宏定义 格式:# define 宏名(参数表)字符串 说明: 1)对带参数的宏展开只是将宏名后括号内的实参 字符串代替# define命令行中的形参。 2)宏定义时,在宏名与带参数的括号之间不应加 空格,否则将空格以后的字符都作为替代字符 串的一部分。 3带参宏定义,形参不分配内存单元,因此不必 作类型定义。(与函数的区别之一) 4带参宏与函数的区别之二 、带参的宏定义和函数不同: 函数调用时,先求实参表达式值,后代入。而带参的宏只是进行简单的字符替换
⑴宏名一般用大写表示,以便与变量名区分。 ⑵使用宏名使程序易读,易修改。 ⑶只作简单的置换,不作正确性检查。 ⑷宏定义不是 C 语句,不必在行末加分号。 ⑸宏名的作用域一般从自定义命令到本源文件结束。 ⑹可以用# undef 命令终止宏定义的作用域。 ⑺宏定义允许嵌套,允许层层置换。 ⑻宏名在源程序中用双引号括起来,则 TC 中预处理不对其作宏代换。 ⑼宏定义与定义的变量不同,宏只作字符替换,不分配内存空间。 ⑽对“输出格式”进行宏定义,可以减少书写麻烦 二、带参数的宏定义 格式:#define 宏名(参数表) 字符串 说明: ⑴对带参数的宏展开只是将宏名后括号内的实参 字符串代替#define 命令行中的形参。 ⑵宏定义时,在宏名与带参数的括号之间不应加 空格,否则将空格以后的字符都作为替代字符 串的一部分。 ⑶带参宏定义,形参不分配内存单元,因此不必 作类型定义。(与函数的区别之一) ⑷带参宏与函数的区别之二 三、带参的宏定义和函数不同: 1、函数调用时,先求实参表达式值,后代入。而带参的宏只是进行简单的字符替换
、函数调用是在程序运行时处理的,分配临时的内存单元。而宏展开则是在编译时进行的,不分配内 存单元,不进行值的传递,也无“返回值” 对函数中的实参和形参都要定义类型,类型应一致。而宏不存在类型问题,宏名和参数无类型,只 个符号代表,展开时代入指定的字符即可。 4、调用函数只可得到一个返回值 而用宏可以设法得到几个结果。 使用宏次数多时,宏展开后使源程序增长, 而函数调用不使源程序变长。 宏替换不占运行时间,只占编译时间 而函数调用则占用运行时间(分配单元、保留现场、值传递、返回) 般用宏代表简短的表达式比较合适 “文件包含”处理 “文件包含”处理是指将指定的被包含文件的全部内容插到该控制行的位置处,使 其成为源文件的一部分参与编译。因此,被包含的文件应该是源文件 通常置于源程序文件的首部,故也称为“头文件” C编译系统提供的头文件扩展名为“,h”,但设计者可根据实际情况,自行确定包含 文件的后缀、名字及其位置。 一般形式,# include“文件名” 或# include<文件名 注:被包含的文件应是源文件,而不应是目标文件。 头文件除了可以包含函数原型和宏定义外,也可以包括结构体类型定义和全局变量定 说明: 1、一个 include命令只能指定一个被包含文件,如果要包含n个文件,要用n个 include命 2、如果文件1包含文件2,而文件2中要用到文件3的内容,则可在文件1中用两个 include 命令分别包含文件2和文件3,而且文件3应出现在文件2之前,即在“filc”中定义:
2、函数调用是在程序运行时处理的,分配临时的内存单元。而宏展开则是在编译时进行的,不分配内 存单元,不进行值的传递,也无“返回值”。 3、对函数中的实参和形参都要定义类型,类型应一致。而宏不存在类型问题,宏名和参数无类型,只 是一个符号代表,展开时代入指定的字符即可。 4、调用函数只可得到一个返回值, 而用宏可以设法得到几个结果。 5、使用宏次数多时,宏展开后使源程序增长, 而函数调用不使源程序变长。 6、宏替换不占运行时间,只占编译时间。 而函数调用则占用运行时间( 分配单元、保留现场、值传递、返回) 一般用宏代表简短的表达式比较合适。 2. “文件包含”处理 “文件包含”处理是指将指定的被包含文件的全部内容插到该控制行的位置处,使 其成为源文件的一部分参与编译。因此,被包含的文件应该是源文件。 通常置于源程序文件的首部,故也称为“头文件”。 C 编译系统提供的头文件扩展名为“.h”,但设计者可根据实际情况,自行确定包含 文件的后缀、名字及其位置。 一般形式,#include “文件名” 或 #include 注:被包含的文件应是源文件,而不应是目标文件。 头文件除了可以包含函数原型和宏定义外,也可以包括结构体类型定义和全局变量定 义等。 说明: 1、一个 include 命令只能指定一个被包含文件,如果要包含 n 个文件,要用 n 个 include 命 令。 2、如果文件 1 包含文件 2,而文件 2 中要用到文件 3 的内容,则可在文件 1 中用两个 include 命令分别包含文件 2 和文件 3,而且文件 3 应出现在文件 2 之前,即在“file1.c”中定义:
# include“file3.h” # include“file2.h” 3、在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。 4、被包含文件(file2.h)与其所在的文件(file1.c),在预编译后已成为同一个文件 5、在# inc lude命令中,文件名可以用双引号或尖括号括起来 者的区别 用尖括号时称为标准方式,系统到存放C库头文件所在的目录中寻找要包含的文件 用双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再按标准方式查找。 # include“c:\ tc\inc lude\myfile.h” /*正确* *include /*正确*/ include<c:\tc\include\std /*错误*/ 用尖括号:带路径:按指定路径去寻找被包含文件,但此时被包含文件不能以“h”结尾,否 则错误 不带路径:仅从指定标准目录下找。 用引号:带路径:按指定路径去寻找被包含文件,不再从当前目录和指定目录下找。 不带路径:先在当前目录下找,找不到再在系统指定的标准目录下找 递归函数 4.4递归函数
#include “file3.h” #include “file2.h” 3、在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。 4、被包含文件(file2.h)与其所在的文件(file1.c),在预编译后已成为同一个文件。 5、在#include 命令中,文件名可以用双引号或尖括号括起来。 二者的区别: 用尖括号时称为标准方式,系统到存放 C 库头文件所在的目录中寻找要包含的文件。 用双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再按标准方式查找。 #include “c:\tc\include\myfile.h” /*正确*/ #include /*正确*/ #include /*错误*/ 用尖括号:带路径:按指定路径去寻找被包含文件,但此时被包含文件不能以“.h”结尾,否 则错误。 不带路径:仅从指定标准目录下找。 用引号: 带路径:按指定路径去寻找被包含文件,不再从当前目录和指定目录下找。 不带路径:先在当前目录下找,找不到再在系统指定的标准目录下找。 递归函数 4 . 4 递归函数