当前位置:首页 >> 工学 >>

C语言讨论课


第六章 函数与定义域
主讲人: 主讲人:刘刚 黄尧 曾楚晟 杜珊

第6章 函数与宏定义
? 6.1 函数的概念 ? 6.2 变量的作用域和储存类型 ? 6.3内部函数与外部函数 ? 6.4递归函数的设计和调用 ? 6.5预处理

6.1 函数的概念
? 6.1 前言 ? 6.1.1 函数的定义 ? 6.1.2 函数的声明和调用 ? 6.1.3 函数的传值方式

6.1 前言
? 在C语言中,函数可分为两类,一类是由系统定义 的标准函数,又称为库函数,其函数声明一般是 放在系统的include目录下以.h为后缀的头文件, 如在程序中要用到某个库函数,必须在调用该函 数之前用#include<头文件名>命令将库函数信息 包含到本程序中。 ? 另一类函数是自定义函数,这类函数是根据问题 的特殊要求而设计的,自定义的函数为程序的模 块化设计提供了有效的技术支撑,有利于程序的 维护和扩充。<重点>

函数调用的简单列子<教材p4>
#include<stdio.h> int add(int x,int y); /*函数声明*/ main() { int a,b,c; printf("Please input value of a and b:\n"); scanf("%d %d",&a,&b); c=add(a,b); /*函数调用*/ printf("max=%d\n",c); } int add(int x,int y) /*函数的定义*/ { return(x+y); }

1.现代形式: [储存类型符] [返回值类型符] 函数名([形参说明表]) { 函数语句体 } 2.古典形式: [储存类型符] [返回值类型符] 函数名([形参表]) 形参说明; { 函数语句体 }

6.1.1 函数的定义

注意;函数返回语句的情况
1)带返回值的定义 int sum1(int m) { int i,sum=0; for(i=1;i<=m;i++) sum=sum+i; return sum; } 2)没有返回值 void sum2(int m) { int I,sum=0; for(i=1;i<=m;i++) sum=sum+i; s=sum; }

完整的源程序 #include <conio.h> #include <stdio.h> int s; int sum1(int m) { int i,sum=0; for(i=1;i<=m;i++) sum=sum+i; return sum; } void sum2(int m) { int i,sum=0; for(i=1;i<=m;i++) sum=sum+i; s=sum; }

main() { int n,s; scanf("%d",&n); s=sum1(n); printf("sum1(n)=%d\n",s ); getch(); sum2(n); printf("sum2(n)=%d\n",s ); getch(); }

1,函数的声明 函数声明语句的一般形式为: [储存类型符] [返回值类型符] 函数名([形参说明表]); 如:int abs_sun(int m,int n); 2.函数的调用 两种形式 1,函数无返回值的函数调用语句 函数名([实参表]); 2, 函数有返回值的函数调用语句 变量名=函数名([实参表]); 注:该变量的类型必须与函数的返回值类型相同。

6.1.2 函数的声明和调用

6.1.3 函数的传值方式
1,定义。<教材P127> 2,自定义函数在程序中使用顺序的两种形 式。 第一种;先进行函数声明,再进行函数调用, 函数定义放在函数调用之前,具体位置与编译环 境有关。 第二种;函数定义放在main函数的前面,在 进行函数调用。在这种情况下,可以不进行函数 声明。

具体程序

6.2 变量的作用域和存储 类型
可能不是很好 还请见谅!

目录
变量的作用域 变量的存储类型

1.变量的作用域
? 局部变量:在函数内部或某个控制块的内部定义的变量为 局部变量: 局部变量,局部变量的有效范围只限于本函数内部, 局部变量,局部变量的有效范围只限于本函数内部,退出 函数,该变量自动失效。 函数,该变量自动失效。局部变量所具有的这种特性使程 序的模块增强了独立性。 序的模块增强了独立性。 ? 全变量:在函数外面定义的变量称为全局变量,全局变量 全变量:在函数外面定义的变量称为全局变量, 的作用域是从该变量定义的位置开始,直到源文件结束。 的作用域是从该变量定义的位置开始,直到源文件结束。 在同一文件中的所有函数都可以引用全局变量。 在同一文件中的所有函数都可以引用全局变量。全局变量 所具有的这种特性可以增强各函数间数据的联系。 所具有的这种特性可以增强各函数间数据的联系。

int i; main() { float a,b; ……. { char s; ……. } function(); } int k; void fuction(); { int m; …… }

局部变量S的 作用域

局部 变量 a,b 的作 用域
全局变量 k的作用 域

全局变 量i的作 用域

局部变量的作 用域

2.变量的存储类型 2.变量的存储类型
? 变量的存储类型指的是变量的存储

属性,它说明变量占用存储空间的 区域。在内存中,供用户使用的存 储区由程序区,静态存储区和动态 存储区3部分组成。变量的存储类 型有auto型,register型,static 型和extern型4种。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

例如: #include <stdio.h> void a( void ); void b( void ); void c( void ); int x = 1; int main() { int x = 5; printf("x in main is %d\n", x ); { int x = 7; printf( "x in inner scope of main is %d\n", x ); } printf( "x in main is %d\n", x ); a(); b(); c(); a(); b(); c(); printf( "\nx in main is %d\n", x ); return 0;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

void a( void ) { int x = 25; printf("\nThis is function a:\n"); printf( "x in a is: %d\n", x ); ++x; printf( "++x in a is: %d\n", x ); } void b( void ) { static int x = 50; printf("\nThis is function b:\n"); printf( "static x in b is: %d\n", x ); ++x; printf( "static ++x in b is:%d\n", x ); } void c( void ) { printf("\nThis is function c:\n"); printf( "global x in c is:%d\n", x ); ++x; printf( "global ++x in c is:%d\n", x ); }

6.3 内部函数与外部函数
? 内部函数:若函数的存储类型为statistic型, 则称其为内部函数或静态函数,它表示在 由多个源文件组成的同一个程序中,该函 数只能在所在文件中使用,在其他文件中 不能使用。 ? 外部函数:若函数的存储类型为extern型, 则称其为外部函数,他表示该函数能被其 它源文件调用。函数的默认存储类型为 extern型。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

/* example6_6.c 调用外部函数*/ #include <stdio.h> extern int mod(int a, int b); /*外部函数声明*/ extern int add(int m, int n); /*外部函数声明*/ main() { int x, y, result1,result2,result; printf("Please enter x and y:\n"); scanf ("%d%d", &x, &y); result1=add(x,y); /*调用file1中的外部函数*/ printf("x+y=%d\n",result1); if (result1>0) result2=mod(x,y); /*调用file2中的外部函数*/ result=result1-result2; printf("mod(x,y)=%d\n",result2); printf("(x+y)-mod(x,y)=%d\n", result); } /* file1.c 外部函数定义 */ extern int add(int m, int n) { return (m+n); } /* file2.c 外部函数定义 */ extern int mod(int a, int b) { return(a%b); }

6.4 递归函数的设计和调用

? 函数的嵌 套调用就 是在调用 一个函数 的过程中 有调用了 另一个函 数。

… main() { float t; int x,y; t=fun1(x,y); … } ... float fun1(int a,int b) { int z; z=fun2(a+b,a-b); … } int fun2(int m,int n) { … } …

? 调用过程按图中箭头方向和顺序进行, 为线性调用,每次调用后,最终回到原 调用点,继续执行下面的语句。

例题:计算 例题: s=1k+2k+3k+……+N ……+N

k

/*功能:函数的嵌套调用*/ #include<stdio.h> #define K 4 #define N 5 long f1(int n,int k) /*计算n的k次方 */ { long power=n; int i; for(i=1;i<k;i++) power *= n; return power; }

long f2(int n,int k) /*计算1到n的k次 方之累加和*/ { long sum=0; int i; for(i=1;i<=n;i++) sum += f1(i, k); return sum; } main() { printf("Sum of %d powers of integers from 1 to %d = ",K,N); printf("%d\n",f2(N,K)); }

? 函数的递归调用就是在调用一个函数的过程中又 出现直接或间接调用该函数本身的调用方式。 ? 在递归调用中,调用函数又是被调用函数,执行 在递归调用中,调用函数又是被调用函数, 递归函数将反复调用其自身。 递归函数将反复调用其自身。每调用一次就进入 新的一层。 新的一层。 ① 直接递归调用:调用函数本身。 ② 间接递归调用:互相调用函数。 ? 为了防止递归调用无终止地进行 , 必须在函数内 为了防止递归调用无终止地进行, 有终止递归调用的手段。 有终止递归调用的手段 。 常用的办法是加条件判 满足某种条件后就不再作递归调用, 断 , 满足某种条件后就不再作递归调用 , 然后逐 层返回。 层返回。

f函数

f1函数

f2函数

调用f函数

调用f2函数

调用f1函数

一个函数能设计成为递归函数必须具备两个条 件: ① 问题的后一部分与原始问题类似。 ② 问题的后一部分是原始问题的简化。 ?

例题:汉诺塔问题

A

B

C

A

B

C

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

/*example6_16.c 用递归算法求解汉诺塔游戏的步骤*/ #include<stdio.h> /*将n个盘子从tower_A塔借助tower_B塔移动到tower_C塔上:*/ void HanoTower(unsigned n,char tower_A,char tower_B,char tower_C); /*移动tower1塔上的一个盘子到tower2)塔上:*/ void move(char tower1,char tower2); int steps=0; main() { unsigned n; printf("Please enter the number of disk:\n"); scanf("%d",&n); /*输入盘子的个数*/ printf("The steps of move:\n"); HanoTower(n,'A','B','C'); /*调用函数,将n个盘子从A塔借 助B塔移动到C塔上*/ printf("The Total steps are: %d\n",steps); }

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

void HanoTower(unsigned n,char a,char b,char c) { steps++; if(n==1) move(a,c); else { HanoTower(n-1,a,c,b); move(a,c); HanoTower(n-1,b,a,c); } } void move(char tower1,char tower2) { printf("form \t%c --> \t%c\n",tower1,tower2);

? }

? 例题:编写一个程序,用于求解一元二次 方程的根。要求求解用一个函数实现,并 且分别用3个函数实现判别式大于0,等于0 和小于0时的运算。

#include<stdio.h> #include<math.h> void answer1(double discr,double a,double b) { double x1,x2; x1=(-b+sqrt(discr))/(2*a); x2=(-b-sqrt(discr))/(2*a); printf("has distinct real roots:%lf,%lf\n",x1,x2); } void answer2(double a,double b) { double x1,x2; x1=x2=-b/(2*a); printf("has two equal roots:%f,%f\n",x1,x2); } void answer3(double discr,double a,double b) { double realpart,imagpart; realpart=-b/(2*a); imagpart=sqrt(-discr)/(2*a); printf("%lf+%lf\n",realpart,imagp art); printf("%lf%lf\n",realpart,imagpart); }

void solution(double a,double b,double c) { double discr; discr=b*b-4*a*c; if(discr>1e-6) answer1(discr,a,b); else if(fabs(discr)<=1e-6) answer2(a,b); else answer3(discr,a,b); } main() { double a,b,c; printf("this is a process for equations result!\n"); printf("please enter the coefficients a,b,c:"); scanf("%lf,%lf,%lf",&a,&b,&c); if(a<1e-6) printf("the equation is not a quadratic!\n"); else solution(a,b,c); return 0; }

6.5

预处理

所谓编译预处理是指,在对源程序进行 编译之前,先对源程序中的编译预处理命令 进行处理;然后再将处理的结果,和源程序 一起进行编译,以得到目标代码。

6.5.1

宏定义

在C语言中,“宏”分为无参数的宏 (简称无参宏)和有参数的宏(简称有 1.无参宏定义的一般格式 无参宏定义的一般格式 参宏)两种。
#define 标识符 语言符号字符串 其中:“define”为宏定义命令;“标识符” 为所定义的宏名,通常用大写字母表示,以便于 与变量区别;“语言符号字符串”可以是常数、 表达式、格式串等。 2.使用宏定义的优点 . (1)可提高源程序的可维护性 (2)可提高源程序的可移植性

例 输入圆的半径,求圆的周长、面积和球的体积。要求使 用无参宏定义圆周率。 /*程序功能:输入圆的半径,求圆的周长、面积和球的体 积。 */ #define PI 3.1415926 /*PI是宏名,3.1415926用来 替换宏名的常数*/ main() { float radius,length,area,volume; printf("Input a radius: "); scanf("%f",&radius); length=2*PI*radius; /* 引 用 无 参 宏 求 周长*/ area=PI*radius*radius; /* 引 用 无 参 宏 求 面积*/ volume=PI*radius*radius*radius*3/4; /*

3.说明 .
(1)宏名一般用大写字母表示,以示与变量区别。但这并非是规定。 (2)宏定义不是C语句,所以不能在行尾加分号。否则,宏展开时, 会将分号作为字符串的1个字符,用于替换宏名。 (3)在宏展开时,预处理程序仅以按宏定义简单替换宏名,而不作 任何检查。如果有错误,只能由编译程序在编译宏展开后的源程序时发 现。 (4)宏定义命令#define出现在函数的外部,宏名的有效范围是:从 定义命令之后, 到本文件结束。通常,宏定义命令放在文件开头处。 (5)在进行宏定义时,可以引用已定义的宏名 。 (6)对双引号括起来的字符串内的字符,即使与宏名同名,也不进 行宏展开。

有参宏定义
1.带参宏定义的一般格式 #define 宏名 形参表 宏名(形参表 形参表) 语言符号字符串 2.带参宏的调用和宏展开 (1)调用格式:宏名 实参表 宏名(实参表 宏名 实参表) (2)宏展开:用宏调用提供的实参字符串, 直接置换宏定义命令行中、相应形参字符串,非 形参字符保持不变。 3.说明 (1)定义有参宏时,宏名与左圆括号之间不 能留有空格。否则,C编译系统将空格以后的所 有字符均作为替代字符串,而将该宏视为无参宏。 (2)有参宏的展开,只是将实参作为字符串,

(3)虽然有参宏与有参函数确实有相似之处,但不同之处更多, 主要有以下几个方面: 1)调用有参函数时,是先求出实参的值,然后再复制一份给形参。 而展开有参宏时,只是将实参简单地置换形参。 2)在有参函数中,形参是有类型的,所以要求实参的类型与其一 致;而在有参宏中,形参是没有类型信息的,因此用于置换的实参, 什么类型都可以。有时,可利用有参宏的这一特性,实现通用函数功 能。 3)使用有参函数,无论调用多少次,都不会使目标程序变长,但 每次调用都要占用系统时间进行调用现场保护和现场恢复;而使用有 参宏,由于宏展开是在编译时进行的,所以不占运行时间,但是每引 用1次,都会使目标程序增大1次。

6.5.2

文件包含

1.文件包含的概念 文件包含是指,一个源文件可以将另一个源文 件的全部内容包含进来。 2.文件包含处理命令的格式 包含文件名” #include “包含文件名” 或 #include < 包含文件名 包含文件名> 包含文件名 两种格式的区别仅在于: (1)使用双引号:系统首先到当前目录下查找 被包含文件,如果没找到,再到系统指定的“包含 文件目录”(由用户在配置环境时设置)去查找。

3.文件包含的优点 一个大程序,通常分为多个模块,并由多个程序员 分别编程。有了文件包含处理功能,就可以将多个模块 共用的数据(如符号常量和数据结构)或函数,集中到 一个单独的文件中。这样,凡是要使用其中数据或调用 其中函数的程序员,只要使用文件包含处理功能,将所 需文件包含进来即可,不必再重复定义它们,从而减少 重复劳动。 4.说明 (1)编译预处理时,预处理程序将查找指定的被包 含文件,并将其复制到#include命令出现的位置上。

(2)常用在文件头部的被包含文件,称为“标题文 件”或“头部文件”,常以“h”(head)作为后缀,简 称头文件。在头文件中,除可包含宏定义外,还可包含 外部变量定义、结构类型定义等。 (3)一条包含命令,只能指定一个被包含文件。如 果要包含n个文件,则要用n条包含命令。 (4)文件包含可以嵌套,即被包含文件中又包含另 一个文件。

6.5.3

条件编译

条件编译可有效地提高程序的可移植性,并广泛地应 用在商业软件中,为一个程序提供各种不同的版本。

1 #ifdef ~ #endif和#ifndef ~ #endif和 #endif命令 #endif命令
1.一般格式 . #ifdef 标识符 程序段1; [#else 程序段2;] #endif 2.功能:当“标识符”已经被 #define命令定义过,则编译程序段1,否 则编译程序段2。

(2)利用条件编译,还可使同一源程序即适合于调 试(进行程序跟踪、打印较多的状态或错误信息),又适 合高效执行要求。 3.关于 关于#ifndef ~ #endif命令 关于 命令 格式与#ifdef ~ #endif命令一样,功能正好与之相反。

2 #if ~ #endif
1.一般格式 一般格式 #if 常量表达式 程序段1; [#else 程序段2;] #endif 2.功能 功能:当表达式为非0(“逻辑 功能 真”)时,编译程序段1,否则编译程序段

输入一行字母字符,根据需要设置条件编译, 例: 输入一行字母字符,根据需要设置条件编译, 使之能将字母全改为大写输出, 使之能将字母全改为大写输出,或全改为小写字 母输出。 母输出。
#include <stdio.h> #define LETTER 1 void main() {char str[20]="C Language",c; int i; i=0; while((c=str[i])!='\0') { i++; #if LETTER if(c>='a' && c<='z') c=c-32; #else if(c>='A' && c<='Z') c=c+32; #endif printf("%c",c); } }


相关文章:
C语言程序设计课程评分
C语言程序设计课程评分_工学_高等教育_教育专区。C 语言程序设计课程评分办法总...然后 全班同学进行讨论,其他组每组选派代表分别进行点评,指出系统的优缺点,并...
c语言教案
信息工程系 课程名称: C 语言学习与应用 任课班级: 任课教师: 15 级计算机应用...教学方法 教学手段 教学方法:演示法、模仿式教学法、案例教学法、练习法和讨论...
c语言课程实训报告
c语言课程实训报告 - C 语言课程实训报告 题 目: 简单计算器 湖北文理学院理工学院 计算机科学与技术 张璟东,卢焓,王叶子,陈聪聪 学院名称: 专业班级: 学生...
《C语言程序设计》课程教学的探讨
C 语言程序设计》课程教学的探讨 文/刘在英张丽晓 作者简介:刘在英(1977.-)...要求学生在课下充分预习,课上围绕思考题展开讨论,每次课分组讨论 1-3 次,每次...
2016c语言课程设计心得体会
C 语言程序设计》这门课,但是我所学的知识最 多也就是在做作业的时候才会用到,平时没有什么练习的机会,这次的课程设计是我第 一次通过自己构思,和同学讨论...
C语言课程设计报告书
C语言课程设计报告书 - 东南大学 C 语言课程设计报告 课程名称: 学院: 计算机综合课程设计 土木工程学院 线性方程组的求解 大二年级 王冰 051144 设计题目: ...
如何把这门C语言的课程学好
如何把这门C语言的课程学好 - 如何把这门 C 语言的课程学好 1.把 C++当成一门新的语言学习(和 C 没啥关系!真的。); 2.看《Thinking In C++》,不要看...
C语言课程设计题目
C语言课程设计题目 - C 语言课程设计题目 C 语言课程设计学生选题说明: 一、设计要求与设计报告? 设计要求: 1、任意选定以下一个题目完成 2、模块化程序设计 3...
C语言课程设计题目
C语言课程设计题目_工学_高等教育_教育专区。C ...测试结果的分析与讨论,测试过程中遇到的主要问题及...(公共课、必修课、选修课), 总学时,授课学时,...
排班系统C语言课程设计
排班系统C语言课程设计_计算机软件及应用_IT/计算机_专业资料。排班系统C语言课程...此外,思考、讨论、查找资料以及调试、修改的过程是非常繁琐的,但是我们要尽量做 ...
更多相关标签: