程序设计入门-C语言基础知识-翁恺-第五周:函数-详细笔记(五)

第五周:函数

5.1 函数

什么是函数?

  • 函数是一块代码,接受零个或多个参数,做一件事情,并返回零个或一个值。
  • 函数声明语法
    返回值 函数名(参数列表){
    函数体
    }
  • 我们前面列子中经常使用的printf和scanf都是函数

调用函数

  • 函数(参数值);
  • ()小括号起到了表示函数调用的重要作用,及时没有参数也需要()
  • 如果有参数,则需要给出正确的数量和顺序
  • 这些值会被按照顺序依次来初始化函数中的参数
  • 调用函数时给的参数叫做实际参数,在函数体内接受到的参数叫形式参数
  • 函数知道每一次是哪里调用它,会返回到正确的地方
  • 可以传递给函数的值是表达式的结果,这包括:
    • 字面量
    • 变量
    • 函数的返回值
    • 计算的结果
  • 调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞
  • 编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的
  • 后续的语言,C++/Java在这方面很严格

从函数中返回值

  • return停止函数的执行,并返回一个值
  • return;
  • return 表达式;
  • 一个函数中可以有多个return语句
  • 不推荐给一个函数多个return不符合单一出口原则,多个return我们再做修改时需要去找所有的出口

没有返回值的函数

  • void 函数名(参数表)
  • 不能使用呆值的return,也可以没有return
  • 调用的时候不能做返回值的赋值
  • tips:如果是有返回值的函数,则必须使用带值的return

5-2 使用函数

  • 把函数的原型声明写在main函数前是因为c的编译器自上而下顺序分析你的代码
  • 函数可以先做原型声明,再定义;
  • 函数的声明和定义必须一致

函数原型

  • 函数头,以分号";"结束,就构成了函数原型
  • 函数原型的目的是告诉编译器这个函数长什么样子
    • 名称
    • 参数(数量及类型)
    • 返回类型
  • 旧的标准习惯把函数原型写在调用它的函数里面,现在一般写在调用它函数的前面。
  • 原型里可以不写参数的名字,但是一般仍然要写上,便于阅读。

参数的传递

  • c语言的函数的参数的传递是对值的传递
  • 每个函数有自己的变了空间,参数也位于这个独立的空间中,和其他函数没有关系
  • 过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫做“实际参数”
  • 由于容易让初学者误会实际参数就是实际在函数中进行计算的参数,误会调用函数的时候把变量而不是值传进去了,所以我们不建议继续用这种种古老的方式称呼它们。
  • 调用参数的时候,我们就叫把值传递进去了,而在函数内部我们就称为参数。

本地变量

  • 函数的每次运行,就产生了衣蛾独立的变量空间,在这个空间中的变量,是函数这次运行所独有的,称为本地变量。
  • 作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以)起作用
  • 对于本地变量,这两个问题的答案是统一的——大括号内(块)

本地变量的规则

  • 本地变量时定义在块内的
    • 块可以是函数的块内
    • 也可以是语句的块内
    • 甚至可以随便拉一大括号定义变量
  • 程序运行进入这个快之前,这个变量不存在,离开这个快,这个变量也消失了
  • 在块外定义的变量在块内仍然有效
  • 在块里面定义了和外面同名的变量则掩盖了外面的
  • 不能在同一块内定义同名的变量
  • 本地变量不会被默认初始化
  • 参数在进入函数的时候就被初始化了

没有参数时

  • 在函数没有参数时一定要使用void填充参数,比如: void f(void);
  • 因为如果不写void就等于告诉编译器,不确定需要什么参数,当调用函数时如果有参数,编译器会默认以为函数的参数的类型都是int ,而且在定义时如果参数不一致编译器也不会报错,因此当定义时变量为double而声明为int调用的结果就不是我们预期的了。
  • 同样如果函数没有声明,编译器会默认认为这个函数是一个 int类型返回值并且有两个int类型参数的函数,为了程序便于阅读,所有的函数在使用前都需要原型声明,再定义。

逗号运算符

  • 调用函数时的逗号和逗号运算符怎么区分?
  • 调用函数时的圆括号里的逗号是标点分隔符,不是运算符
    • f(a,b) 标点分割符,分割多个参数
    • f((a,b)) 算术运算符
  • (a,b) 逗号运算符自左向右运算,先计算左边表达式的值,再计算右边表达式的值,最后的结果返回右边表达式的值。比如 (a=3+2,a-5)的结果是 0

函数里面的函数

  • c语言不允许函数的嵌套定义

奇怪的写法

  • int i,j,sum(int a,int b); 虽然c语言允许这么做,但是不推荐这种写法,不易于阅读。
  • return (i); 返回一个i的值,同样不推荐,理由同上。

关于main

  • int mian()也是一个函数
  • 要不要写成int main(void)? 可以写成void,并且在main函数执行前程序还有些准备的工作,当执行好这些准备工作后才会调用main函数
  • return的0有人看吗?
    • Windows: if errorlevel 1...
    • Unix Bash: echo $?
    • Csh: echo $status
    • return的值在各个系统都是可以得到的,有人会根据返回值来判断程序是否出错等,因此return 0 是有意义的。

5.3 课后习题

1 .题目内容: 题目内容:
每个非素数(合数)都可以写成几个素数(也可称为质数)相乘的形式,这几个素数就都叫
做这个合数的质因数。比如,6 可以被分解为 2x3,而 24 可以被分解为 2x2x2x3。
现在,你的程序要读入一个[2,100000]范围内的整数,然后输出它的质因数分解式;当
读到的就是素数时,输出它本身。
输入格式:
一个整数,范围在[2,100000]内。
输出格式:
形如:
n=axbxcxd

n=n
所有的符号之间都没有空格,x 是小写字母 x。
输入样例:
18
输出样例:
18=2x3x3

题目分析:

  • 我们前面写过判断素数的功能,可以把他封装成一个函数。
  • 这里用用户输入的有两种情况,输入的是一个素数或者合数。
  • 素数十分好处理,我们直接输出这个数本身即可。
  • 如果是合数怎么算出这个合数的质因数呢?我们有三个未知的问题,一是不知道这个合数有多少个质因数,并且也不知道这些质因数的值和组合方式。
  • 我们可以采取笨一点的办法,
    1. 用这个合数num和最小素数2做取余,如果结果不为0,则表示除不尽继续用下一个素数3做整除,一直到能整除为止,算出一个结果。
    2. 将结果赋值给num,如果num还是合数回到第1步,否则我们就已经把最初的素数num拆分成n个相乘的素数了,也就是这个合数的质因数。
  • 因此我们还需要一个取第x个素数的值一个函数,x>0

程序实现:

#ifndef getPrimeFactor_h

#define getPrimeFactor_h
#include <stdio.h>
int isPrime(int number);
void getPrimeFactor(void);
int getPrimeNumber(int index);
#endif



#include "getPrimeFactor.h"

int isPrime(int number)
{
 int isPrimeNumber = 1;
 int middle = 0;

 if (number == 1)
 {
  isPrimeNumber = 0;
 }
 else
 {
  int i = 2;
  middle = number / 2;
  for (; i <= middle; i++)
  {
   if (number % i == 0)
   {
    isPrimeNumber = 0;
    break;
   }
  }
 }

 return isPrimeNumber;
}

int getPrimeNumber(int index)
{
 int isPrimeNumber = 1;
 int middle = 0;
 int primeNumber = 2;
 int i, j;
 int currentIndex = 0;

 for (j = 2; j < 100000; j++)
 {
  //重置素数状态
  isPrimeNumber=1;
  middle = j / 2;
  for (i = 2; i <= middle; i++)
  {
   if (j % i == 0)
   {
    isPrimeNumber = 0;
    break;
   }
  }
  if (isPrimeNumber)
  {
   primeNumber = j;
   currentIndex++;
  }
  if (index == currentIndex)
  {
   break;
  }
 }
 return primeNumber;
}

void getPrimeFactor(void)
{
 int number = 0;
 int currentPrimeNum = 2; //当前素数
 int primeIndex = 1;	//当前素数的序号,从1开始1,2,3...

 printf("请输入一个范围在[2,100000]的整数");
 scanf("%d", &number);

 if (isPrime(number))
 {
  printf("%d=%d", number, number);
 }
 else
 {
  printf("%d=", number);
  do
  {
   while (number % currentPrimeNum != 0 && currentPrimeNum < number)
   {
    primeIndex++;
    currentPrimeNum = getPrimeNumber(primeIndex);
   }
   number /= currentPrimeNum;
   printf("%dx", currentPrimeNum);
   //当number值改变重置状态
   currentPrimeNum = 2;
   primeIndex = 1;
  } while (!isPrime(number));
  printf("%d", number);
 }
}


#include <stdio.h>
#include "getPrimeFactor.h"

int main(int argc, char *argv[])
{
 getPrimeFactor();
 return 0; 
}

测试样例:

请输入一个范围在[2,100000]的整数18
18=2x3x3
--------------------------------
Process exited after 1.221 seconds with return value 0
请输入一个范围在[2,100000]的整数100
100=2x2x5x5
--------------------------------
Process exited after 1.691 seconds with return value 0
请输入一个范围在[2,100000]的整数108
108=2x2x3x3x3
--------------------------------
Process exited after 2.766 seconds with return value 0

2.题目内容:
一个正整数的因子是所有可以整除它的正整数。而一个数如果恰好等于除它本身外的因子之
和,这个数就称为完数。例如 6=1+2+3(6 的因子是 1,2,3)。
现在,你要写一个程序,读入两个正整数 n 和 m(1<=n<m<1000),输出[n,m]范围内
所有的完数。
提示:可以写一个函数来判断某个数是否是完数。
输入格式:
两个正整数,以空格分隔。
输出格式:
其间所有的完数,以空格分隔,最后一个数字后面没有空格。如果没有,则输出一个空行。
输入样例:
1 10
输出样例:
6

题目分析:

  • 我们需要输出n到m范围内所有的完数。
  • 如何判断一个数n是否是完数呢?我们用n做取余运算,从1开始取余n/2,这期间所有取余为0的数就是这个数的因子,然后我们再对他们对累加,如果累加的和恰好等于n本身那么这个数就是一个完数。

程序实现:

#ifndef getPerfectNumberRange_h

#define getPerfectNumberRange_h
#include <stdio.h>
void getPerfectNumberRange(void);
int getIsPerfectNumber(int number);
#endif



#include "getPerfectNumberRange.h"

int getIsPerfectNumber(int number)
{

    int sum = 0;
    int isPerfectNumber = 0;
    for (int i = 1; i <= number / 2; i++)
    {

        if (number % i == 0)
        {
            sum += i;
        }
    }

    if (sum == number)
    {
        isPerfectNumber = 1;
    }

    return isPerfectNumber;
}

void getPerfectNumberRange(void)
{

    int n, m;
    int isHaveAnyPerfectNumber = 0;
    printf("请输入两个正整数n和m,(1<=n<m<1000)");
    scanf("%d %d", &n, &m);

    for (int i = n; i <= m; i++)
    {

        if (getIsPerfectNumber(i))
        {
            printf("%d", i);

            if (isHaveAnyPerfectNumber == 0)
            {
                isHaveAnyPerfectNumber = 1;
            }

            if (i < m)
            {
                printf(" ");
            }
        }

       
    }

    if (isHaveAnyPerfectNumber == 0)
    {
        printf("\n");
    }
}




#include <stdio.h>
#include "getPerfectNumberRange.h"

int main(int argc, char *argv[])
{
 getPerfectNumberRange();
 return 0; 
}

测试样例:

请输入两个正整数n和m,(1<=n<m<1000)1 100
6 28
--------------------------------
Process exited after 1.984 seconds with return value 0

请输入两个正整数n和m,(1<=n<m<1000)1 5


--------------------------------
Process exited after 4.39 seconds with return value 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值