C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬 jfliu@swufe.edu.cn.

Slides:



Advertisements
Similar presentations
电子商务专业人才培养方案 五年制高职. 一、招生对象、学制与办学层次  (一)招生对象:初中毕业生  (二)学制:五年  (三)办学层次:专科.
Advertisements

主讲:王幸民 理学院计算机基础教学部.
C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
《C语言程序设计》复习
C语言程序设计 主讲教师 :张群燕 电话:
培养目标 1.建立基本的程序设计概念体系,掌握基础程序设计方法。
第一章 C语言概述 计算机公共教学部.
项目2-1 店铺的定位.
你 今 天 累 吗 ? 坪山高级中学心理教师 张婧乔.
第九章 指针 目录 指针与指针变量的概念 变量的指针和指向变量的指针变量 数组的指针和指向数组的指针变量
第4章 选择结构程序设计 在现实生活中,需要进行判断和选择的情况是很多的 如果你在家,我去拜访你 如果考试不及格,要补考
C语言程序设计 第八章 函数.
第5章 函数与模块化设计 学习目的与要求: 掌握函数的定义及调用方法 理解并掌握参数的传递方法 理解函数的嵌套与递归调用
高级语言程序设计 主讲人:陈玉华.
第5章 函数与预处理 《 C语言程序设计》 (Visual C++ 6.0环境) 本章导读
第一章 C语言概述.
由C程序结构所知,一个完整的C语言程序是由一个且只能有一个main()函数(又称主函数)和若干个其他函数组合而成的。而前面各章仅学习main()函数的编程,本章将介绍其他函数的编程,包括其他函数的定义、调用、参数传递及变量的作用域等。
第4章 函数与预处理 4.1 概述 4.2 定义函数的一般形式 4.3 函数参数和函数的值 4.4 函数的调用 *4.5 内置函数
Chap 10 函数与程序结构 10.1 函数的组织 10.2 递归函数 10.3 宏定义 10.4 编译预处理.
第4章 选择结构程序设计 4.1 选择结构和条件判断 4.2 用if语句实现选择结构 4.3关系运算符和关系表达式
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
QQ: 李祥 QQ: 欢迎多种方式的学习交流,祝大家学有所成.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第八章 函数.
第6章 函数 学习的意义.
6 使用者函數 6.1 函數定義 宣告函數 呼叫函數 呼叫多個函數 6-6
C语言 程序设计基础与试验 刘新国、2012年秋.
第八章 使用指针.
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
第三章 顺序结构程序设计 主讲教师 贾月乐 电话:
第七章 函数及变量存贮类型 7.1 函数基础与C程序结构 7.2 函数的定义和声明 7.3 函数的调用 7.4 函数的嵌套与递归
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
第8章 函数 函数参数和函数的值 概述 函数定义的一般形式 函数的调用(嵌套调用、递归调用) 数组作为函数参数
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
C语言复习3----指针.
第 二 章 数据类型、运算符与表达式.
C语言复习2----函数.
第一章 程序设计和C语言 主讲人:高晓娟 计算机学院.
C语言程序示例: 1.输入10个数,按从小到大的顺序排序。 2.汉诺塔问题。.
C程序设计.
函数 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点: 开发方法: 自上向下,逐步分解,分而治之
Oop8 function函式.
第八章 指標 (Pointer).
指標
Chap 5 函数 5.1 计算圆柱体积 5.2 使用函数编写程序 5.3 变量与函数.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
C程序设计.
第5章 函 数.
第一章 C语言概述 教师:周芸.
C语言程序设计 李祥 QQ:
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第7章 程序的结构 四、生存期与存储属性 五、extern关键字与外部连接属性 六、static关键字与内部连接属性.
第二章 类型、对象、运算符和表达式.
第二章 基本数据类型 ——数据的表示.
本节内容 函数嵌套调用的内存布局 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第四章 函数 丘志杰 电子科技大学 计算机学院 软件学院.
第五章 逻辑运算和判断选取控制 §5.1 关系运算符和关系表达式
第十二章 位运算.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
单片机应用技术 (C语言版) 第4章 C51程序设计入门
第三章 高级函数特性.
C/C++基礎程式設計班 C語言入門、變數、基本處理與輸入輸出 講師:林業峻 CSIE, NTU 3/7, 2015.
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
C程序设计 复习 1、计算机系统的组成 外部设备:输入、输出设备(同人打交道《十进制》)
函式庫補充資料 1.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬 jfliu@swufe.edu.cn

程序设计的艺术 结构化程序设计 自顶向下 逐步细化 模块化设计 结构化编码 函数(function)是模块化设计的体现

用函数解决问题的要点 分而治之 复用 信息隐藏 函数把较大的任务分解成若干个较小的任务 程序员可以最大程度利用其他人的代码,而不需要做起从头 设计得当的函数可以把具体操作细节隐藏掉,从而使整个程序结构清楚

例 5.1函数示例 #include <stdio.h> void main( ) { void printstar( ); /*函数声明*/ void print_message( ); /*函数声明*/ printstar( ); print_message( ); printstar( ); } void printstar( ) printf("* * * * * * * * * * * * * * * * \n"); void print_message( ); printf(" How do you do! \n");

C中的函数(Function) 说明: 一个C程序由一个或多个源程序文件组成。 一个源程序文件由一个或多个函数组成。 C程序的执行从main函数开始,调用其他函数后流程回到main函数,在main函数中结束整个程序运行。

函数的分类 函数的使用 主函数main 标准函数,即库函数 用户自定义函数 函数的形式 有参函数 无参函数

无参函数的定义 无参函数的定义形式 类型标识符 函数名( ) { 语句 } 例:void print_message( ) {   类型标识符 函数名( ) { 语句 } 例:void print_message( )   {     printf(" How do you do! \n);   }

有参函数的定义 类型标识符 函数名(形式参数列表 ) { 语句 } 例:int max(int x,int y) int z; 类型标识符 函数名(形式参数列表 ) {   语句 } 例:int max(int x,int y) int z; z=x>y?x:y; return z;

关于函数 函数的类型实际上是返回值的类型,即return语句中返回的数据的类型。如果不需要返回值,应该用void标识。 函数中定义的变量只能在函数内部使用,称内部变量。 形式参数表中的变量也是一种内部变量 形式参数的定义 int max(int x,int y) { … }

函数的命名和参数 函数的命名规则 函数参数: 形式参数 (形参): 实际参数(实参): 在定义函数时,函数名后面括号中的变量名 在主调函数中调用一个函数,调用函数名后面括号中的参数(或表达式)

例5.2实际参数与形式参数 #include <stdio.h> void main( ) { int max(int x,int y); /*max函数的声明*/ int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b);      /*a,b为实际参数*/ printf("Max is %d",c); } int max(int x,int y) /*x,y为形式参数*/ int z; z=x>y?x:y; return z;

关于形参与实参的说明 形参只有在发生函数调用时才临时分配内存单元,调用结束后立即释放 定义函数时必须指定形参的类型 实参可以是常量、变量、表达式、数组名;形参只能是变量 实参与形参的数据传递是“值传递”,即单向传递 实参与形参的类型应相同或赋值兼容(复习2.7)

函数的返回值 返回值通过return语句获得。return语句的作用就是将被调用函数中的一个特定的值返回给主调函数。 一个函数可以没有return语句(void型的函数)。return语句后面的括号可要可不要。 return后面的值可以是常量,变量或者表达式。 int max(int x,int y) { return (x>y? x:y); }

函数的返回值 函数的类型就是返回值的类型,在定义函数时指定。如int max(int x,int y) 函数的类型和return中的表达式的类型相一致。 用“void”表示函数不返回值,为“无类型”或”空类型”。否则的话,函数可能返回一个不确定的值。 例:void printstar() { printf("* * * * * * * * * * * * * * * * \n"); }

一个好习惯 /* 函数功能:实现××××功能 函数参数:参数1,表示××××× 参数2,表示××××× 函数返回值: ××××× */ 返回值类型 函数名(形式参数表) { 函数体 return 表达式; }

函数示例 /* 函数功能: 计算平均值 函数入口参数: 整型x,操作数1 整型y,操作数2 函数返回值: 平均值,整型 */ 函数功能: 计算平均值 函数入口参数: 整型x,操作数1 整型y,操作数2 函数返回值: 平均值,整型 */ int Average(int x, int y) { int result; result = (x + y) / 2; return result; }

函数调用 函数调用的一般形式 提供的实际参数个数、类型、顺序应与定义时形参相同 函数调用过程: 函数名 (实际参数列表); 暂时中断主调函数的运行,转向被调函数 为被调函数的形参分配内存单元 计算主调函数实参的值,并传递给对应的形参 执行被调函数的函数体 释放被调函数形参的内存单元 返回主调函数,继续运行…

函数调用过程示例 void main() { int a = 12; int b = 24; int ave; int Average(int x, int y) { int result; result = (x + y) / 2; return result; } main() int a = 12; int b = 24; int ave; ave = Average(a, b); printf(……); 数据传递 执行顺序 void main() { int a = 12; int b = 24; int ave; ave = Average(a, b); printf("Average of %d and %d is %d.\n", a, b, ave); }

函数调用方法 1. 函数语句(后面加“;”) 如:printstar( ); 2. 函数表达式(出现在一个表达式中) 1. 函数语句(后面加“;”) 如:printstar( ); 2. 函数表达式(出现在一个表达式中) 如:c=2*max(a,b); 3. 函数参数(作为实参出现) 如:m=max(a,max(b,c)); 注:函数调用作为函数的参数,实质上也是函数表达式的一种形式

函数调用的条件 在一个函数中调用另一个函数需要哪些条件? 被调用的函数必须是已经存在的函数(库函数或用户自定义的函数) 使用库函数:文件中应用#include命令包含相关的头文件 使用自定义函数:如果函数定义在后,而调用在前的话,调用前必须先对函数进行声明

例 5.3 #include <stdio.h> void main( ) { float add(float x, float y); /*函数声明*/ float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); /* 函数调用*/ printf("sum is %f",c); } float add(float x,float y) /* 函数定义*/ float z; z=x+y; return z;

函数声明 “声明”的作用是将被调用函数的信息告之编译系统,帮助编译系统进行类型检查 函数声明又称为函数原型. 上例中的函数声明可以使用以下两种: float add(float x,float y); float add(float, float); 对被调函数不用进行声明的情况: 1. 被调函数的定义出现在主调函数之前 2. 在主调函数之前已经进行了声明。

例 5.4 #include <stdio.h> float add(float x,float y) /* 函数定义*/ { float z; z=x+y; return z; } void main( ) float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); /* 函数调用*/ printf("sum is %f",c);

例 5.5 #include <stdio.h> float add(float x,float y); /*函数声明*/ void main( ) { float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); /* 函数调用*/ printf("sum is %f",c); } float add(float x,float y) /* 函数定义*/ float z; z=x+y; return z;

函数的嵌套调用 C语言函数是互相平行的,独立的。 C语言允许函数嵌套调用(多层调用),即函数甲调用函数乙,而函数乙又调用了函数丙。 main() { … a(); } a 函数 b(); return; b函数 ① ③ ④ ⑤ ⑥ ⑦ ②

函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。 f函数 f1函数 f2函数 不能无终止的自身调用 递归调用应该有终止条件

例5.6 有5个人坐在一起,问第5个人岁数,他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,又说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大? 这可以看作是一个递归问题. 令求第n个人年龄的函数是age(n)

求第5个人年龄的过程 age(5)=18 age(5)=age(4)+2 age(4)=age(3)+2 age(3) =age(2)+2 当n>1时,age(n)= age(n-1)+2; 当n=1时,age(n)=10

#include <stdio.h> int age(int n) { int c; if(n==1) c=10; else c=age(n-1)+2; return c; } void main( ) printf("%d",age(5));

函数调用过程 age函数共被调用5次 只有一次是主函数调用 其余4次是函数递归调用 age函数 n=5 age函数 n=4 age函数 main age(5) 输出age(5) c= age(4)+2 c= age(3)+2 c= age(2)+2 c= age(1)+2 c= 10 age(5)=18 age(4)=16 age(3)=14 age(2)=12 age(1)=10

作业 将密码机程序的加密和解密功能用函数来实现 使用函数的递归调用实现求n! 汉诺塔问题 来源于印度的一个古老的传说。开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒,第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一个小,依次叠上去,庙里的众僧不倦地把它们一个个地从这根棒搬到另一根棒上,规定可利用中间的一根棒作为帮助,但每次只能搬一个,而且大的不能放在小的上面。

变量的类型

局部变量 局部变量 在函数内定义的变量是内部变量,它只在本函数范围内有效,称 “局部变量”。如: float f1(int a)   /*函数f1*/ { int b,c;    … } char f2(int x,int y) /*函数f2*/ {int i,j; void main( ) /*主函数*/ {int m,n; a,b,c 有效 x,y,i,j有效 m,n有效

关于局部变量 主函数main中定义的变量也只在主函数中有效。 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。 形式参数也是局部变量。 更小的局部变量:在函数内部某个复合语句中定义变量,它们只在该复合语句中有效。

void main( )          { int a,b; … int c; c=a+b; } C在此范围内有效 a,b在此范围内有效

全局变量 在函数之外定义的变量称为外部变量或称全局变量 全局变量可为本文件中的所有函数共用 它的有效范围是从定义变量的位置开始到本源程序文件结束

int p=1,q=5; /*全局变量*/ float f1(int a) { int b,c; … } char c1,c2; void main( ) { int m,n; { int w; w=m+n; a,b,c有效 全局变量 的作用范围 全局变量 有效 p,q c1,c2 w m,n 有效

例5.7 #include <stdio.h> void GlobalPlusPlus(); main() { int global = 1; printf("Before GlobalPlusPlus(), it is %d\n", global); GlobalPlusPlus(); printf("After GlobalPlusPlus(), it is %d\n", global); } /* 函数功能: 对局部变量global加1,并打印加1之前与之后的值 函数入口参数: 无 函数返回值: 无 */ void GlobalPlusPlus() printf("Before ++, it is %d\n", global); global++; printf("After ++, it is %d\n", global); Before GlobalPlusPlus(), it is 1 Before ++, it is 1 After ++, it is 2 After GlobalPlusPlus(), it is 1

例5.8 #include <stdio.h> int global; /*定义全局变量*/ void GlobalPlusPlus(void); main() { global = 1; printf("Before GlobalPlusPlus(), it is %d\n", global); GlobalPlusPlus(); printf("After GlobalPlusPlus(), it is %d\n", global); } /* 函数功能: 对全局变量global加1,并打印加1之前与之后的值 函数入口参数: 无 函数返回值: 无 */ void GlobalPlusPlus(void) printf("Before ++, it is %d\n", global); global++; printf("After ++, it is %d\n", global); Before GlobalPlusPlus(), it is 1 Before ++, it is 1 After ++, it is 2 After GlobalPlusPlus(), it is 2

关于全局变量 全局变量的作用: 增加了函数之间数据联系的渠道(return只能带回一个值) 建议非必要时不要使用全局变量 如果在同一个源文件中,全局变量与局部变量同名,则局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用

外部变量与局部变量同名 int a=3,b=5; void max(int a,int b) { int c; c=a>b?a:b; return(c); } void main( ) int a=8; printf("%d",max(a,b)); }   局部a 局部b 局部a 全局b

变量的存储类别 存储类别指数据在内存中存储的方式,即编译器为变量分配内存的方式,它决定变量的生存期。 动态存储 静态存储 根据需要临时分配存储空间,离开即释放 静态存储 在程序运行期间分配固定的存储空间,不释放 变量的作用域是从空间角度来看的。 静态、动态存储方式是从时间角度来看的

动态存储与静态存储 运行一个程序时,操作系统会为这个程序分配3种内存区域(3部分): (1)程序区(存放二进制程序代码) (2)静态存储区 (3)动态存储区 程序区 静态存储区 全局变量、 静态变量 动态存储区 形参、自动变量、函数调用的现场等

动态存储与静态存储 全局变量存放在静态存储区中,在程序开始时即分配存储空间,一直到程序结束时才释放。程序执行过程中它们占据固定的内存单元,不是动态地分配和释放。 在动态存储区中存放以下数据: a 函数形参 b 自动变量(普通的局部变量) c 函数调用时的现场保护和返回地址 在函数调用开始时分配在动态存储区中,并且在函数调用结束时释放这些空间。

变量存储类型 变量的存储类别包括两大类: 具体包含4种: 静态存储类 动态存储类。 auto(自动变量) static(静态变量) register(寄存器变量) extern(外部变量)

auto变量 函数中的局部变量,包括函数的形参,如果不专门声明,都是auto变量。通常,auto可以省略。 int f(int a) { int b; ....... }

static变量 有时候,我们希望函数中的局部变量的值,在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次函数调用的时候继续使用这个值。这种变量就叫做局部静态变量,用static来声明。

例 5.9静态变量的值 #include <stdio.h> int f(int a) { auto int b=0; static c=3; b=b+1; c=c+1; return (a+b+c); } void main( ) int a=2,i; for(i=0;i<3;i++) printf(“%d”,f(a)); a+b+c 2+1+4 2+1+5 2+1+6

关于静态变量 静态局部变量在程序整个运行期间都不释放,它属于静态存储类别,在静态存储区中分配存储空间(注意和auto变量区别) 静态局部变量是在编译时赋初值,只赋初值一次,以后每次调用函数时不再重新赋初值而使用上次函数调用时保留的值 如果定义时不赋初值,编译器会自动对静态局部变量赋初值0 虽然静态局部变量在函数调用结束时不释放,但不能被其他函数引用。

静态变量与全局变量 静态变量和全局变量都是静态存储类型 程序区 静态存储区 动态存储区 自动初始化为0 从静态存储区分配,生存期为整个程序运行期间 但作用域不同 程序区 静态存储区 全局变量、 静态变量 动态存储区 形参、自动变量、函数调用的现场等

寄存器变量(register) 寄存器的存储速度远高于内存的存储速度(寄存器在cpu内部,读取的速度比内存高很多) register int i; 现代编译器有能力自动把普通变量优化为寄存器变量,并且可以忽略用户的指定,所以一般无需特别声明变量为register

用extern声明外部变量 外部变量就是全局变量, 用extern声明以扩展全局变量的作用域 int max(int x,int y) { int z; z=x>y?x:y; return(z); } main( ) extern A,B; /*外部变量声明*/ printf("%d",max(A,B)); int A=13,B=-8; /*定义全局变量*/

用static和extern定义函数 根据函数能否被其他源文件调用,将函数分为外部函数和内部函数 外部函数:[extern] int fun(int a,int b) 那么这个函数可以被其他源文件所调用。 在需要调用此函数的源文件中,首先用extern对这个函数进行声明,表明这个函数是在其他文件中定义的。 内部函数:当一个函数只能被本文件中其他函数调用,这个函数称为内部函数,定义时使用static。 static int fun(int a,int b)

模块化程序设计方法 什么时候需要模块化? 要尽可能复用其它人的现成模块。 某一功能,如果重复实现3遍以上,即应考虑模块化,将它写成通用函数,并向小组成员发布 要尽可能复用其它人的现成模块。

模块化程序设计方法 功能分解 模块分解的原则 设计好模块接口 自顶向下、逐步细化的过程 保证模块的相对独立性 模块的实现细节对外不可见 高聚合、低耦合 模块的实现细节对外不可见 外部:关心做什么 内部:关心怎么做 设计好模块接口 接口是指罗列出一个模块的所有的与外部打交道的变量等 定义好后不要轻易改动 在模块开头(文件的开头)进行函数声明

函数设计的原则 函数的功能要单一,不要设计多用途的函数 函数的规模要小,尽量控制在50行代码以内 参数和返回值的规则 1986年IBM在OS/360的研究结果:大多数有错误的函数都大于500行 1991年对148,000行代码的研究表明:小于143行的函数比更长的函数更容易维护 参数和返回值的规则 参数要书写完整,不要省略 没有参数和返回值时,用void填充 每个函数只有一个入口和一个出口,尽量不使用全局变量 尽量少用静态局部变量,以避免使函数具有“记忆”功能

模块和链接 优点: 当一个文件的代码被修改后,不必对所有程序重新编译,从而节省了程序的编译时间。 使程序更宜于维护,给多个程序员共同编制一个大型项目的代码提供了方便手段。

这一章我们学习了 函数的定义、调用 变量的作用域、存储类别 进一步理解程序设计原则