本课程来自中国大学MOOC中《C语言程序设计精髓》 (哈尔滨工业大学)
本讲将介绍一些关于函数的基础知识:
1. 函数的调用
在程序设计中,使用函数有利于模块化编程。函数需要经过定义后才能调用,下面仅介绍一下函数调用的过程。
每次执行函数调用时,有:
- 进行现场保护并为函数内的局部变量(包括形参)分配内存
- 把实参值复制一份给形参,单向传值(实参 → 形参)
- 实参与形参的数目、类型和顺序要一致
从函数退出时 ,有:
- 根据函数调用栈中保存的返回地址,返回到本次函数调用的地方
- 把函数值返回给主调函数,同时把控制权还给调用者
- 收回分配给函数内所有变量(包括形参)的内存
举个例子,下面函数的功能是计算平均值:
#include <stdio.h>
float Average(int x, int y) ; /* compute average */
int main()
{
int a, b ;
scanf("%d %d",&a, &b) ;
printf("avg = %.2f", Average(a, b)) ;
return 0 ;
}
float Average(int x, int y)
/* compute average of x and y */
{
float avg ;
avg = (x + y) / 2.0 ;
return avg ;
}
1 2
avg = 1.50
在主函数main()
中,利用Average()
函数计算平均值,在控制的传递过程中,首先执行函数main()
中的语句,执行到Average()
函数处时,将控制传递至Average()
函数,并为其分配空间,然后便执行Average()
函数中的语句,之后从Average()
函数返回,将控制传递给主函数main()
,最后函数运行结束。在整个过程中,有函数参数、函数控制流以及函数返回值的传递,本质上看,函数的调用也是控制流改变的一种方式,同选择、循环结构有一定的相同之处。
2. 断言以及程序的安全
对于程序设计而言,程序的效率高很重要,但是程序的正确性则更为重要,因为错误的程序无论效率多高都无法得到正确的结果。
2.1 断言
void assert (int expression);
Evaluate assertion
If the argument expression of this macro with functional form compares equal to zero (i.e., the expression is false), a message is written to the standard error device and abort is called, terminating the program execution.
断言assert()
在<assert.h>
中定义的宏,使用该宏时,则会检测expression
的真假:
expression
为真,无声无息;expression
为假,中断程序。
可以认为其相当于if(!expression) exit(1) ;
下面给出一个例子:
#include <stdio.h>
#include <assert.h>
int main()
{
int x, y ;
scanf("%d %d",&x, &y) ;
assert(y != 0) ; /* if y == 0, exit . */
printf("%.2f\n", (float)x/y ) ;
return 0 ;
}
1 0
Assertion failed: y != 0, file :\Documents\Desktop\myproject\Cstudy\Cprogram\main.c, line 7
虽然这样可以禁止y == 0
的情况,但assert()
仅用于调试程序,不能作为程序的功能,故建议使用assert()
的情况有以下几种:
- 检查程序中的各种假设的正确性
- 证实或测试某种不可能发生的状况确实不会发生
如下面的情况:
int MakeNumber(void)
{
int number;
number = rand() % 100 + 1;
assert(number >= 1 && number <= 100);
return number;
}
值得注意的是,assert()
在函数的调试阶段起作用:
- Debug版本
用于内部调试——assert
是仅在Debug版本中起作用 - Release版本
发行给用户使用——编译器会跳过assert
不生成检查代码
2.2 程序的安全性
程序的安全性是一个大的主题,在此,我们考虑下面的例子来介绍它:
交换两个值,下面有两种方法:
/* method 1 */
t = a ;
a = b ;
b = t ;
/* method 2 */
a = a + b ;
b = a – b ;
a = a – b ;
已知的结论是method 1
是优于method 2
的,那么请问method 2
有什么问题?欢迎在评论区讨论!
3. 编程测验
1.三位阶乘和数
题目内容
假设有这样一个三位数m,其百位、十位和个位数字分别是a, b, c
,如果m = a!+b!+c!
,则这个三位数就称为三位阶乘和数(约定0!=1
)。请编程计算并输出所有的三位阶乘和数。
函数原型:long Fact(int n);
函数功能:计算n的阶乘
#include <stdio.h>
long Fact(int n) ;
int main()
{
int m, a, b, c ;
for(m=100; m<1000; m++)
{
a = m / 100 ; /* the hundreds place of m */
b = (m / 10) % 10 ; /* the tens place of m */
c = m % 10 ; /* the ones place of m */
if(m == (Fact(a)+Fact(b)+Fact(c)))
{
printf("%d\n", m) ;
}
}
return 0 ;
}
long Fact(int n)
/* compute factorial of n */
{
long sum = 1 ;
int i ;
for(i = n; i >= 1; i--)
{
sum *= i ;
}
return sum ;
}
145
4. 往期回顾
1.【学习笔记】C语言程序设计(第一周):变量和常量
2.【学习笔记】C语言程序设计(第二周):C运算符和表达式
3.【学习笔记】C语言程序设计(第三周):输入和输出
4.【学习笔记】C语言程序设计(第四周):选择结构
5.【学习笔记】C语言程序设计(第五周):循环结构
6.【学习笔记】C语言程序设计(第五周补充):循环结构练习题