【学习笔记】C06 C语言程序设计(第六周):函数

本课程来自中国大学MOOC中《C语言程序设计精髓》 (哈尔滨工业大学)

  本讲将介绍一些关于函数的基础知识:

1. 函数的调用

  在程序设计中,使用函数有利于模块化编程。函数需要经过定义后才能调用,下面仅介绍一下函数调用的过程。
  每次执行函数调用时,有:

  1. 进行现场保护并为函数内的局部变量(包括形参)分配内存
  2. 把实参值复制一份给形参,单向传值(实参 → 形参)
  3. 实参与形参的数目、类型和顺序要一致

  从函数退出时 ,有:

  1. 根据函数调用栈中保存的返回地址,返回到本次函数调用的地方
  2. 把函数值返回给主调函数,同时把控制权还给调用者
  3. 收回分配给函数内所有变量(包括形参)的内存

  举个例子,下面函数的功能是计算平均值:

#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的真假:

  1. expression为真,无声无息;
  2. 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()的情况有以下几种:

  1. 检查程序中的各种假设的正确性
  2. 证实或测试某种不可能发生的状况确实不会发生

  如下面的情况:

int MakeNumber(void)
{
    
    
	int number;
	number = rand() % 100 + 1;
	assert(number >= 1 && number <= 100);
	return number;
}

  值得注意的是,assert()在函数的调试阶段起作用:

  1. Debug版本
    用于内部调试——assert是仅在Debug版本中起作用
  2. 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语言程序设计(第五周补充):循环结构练习题

猜你喜欢

转载自blog.csdn.net/Stu_YangPeng/article/details/114211105