C语言程序设计-8 函 数

8.1 概述

在前面已经介绍过,C源程序是由函数组成的。虽然在前面各章的程序中大都只有一个
主函数 main(),但实用程序往往由多个函数组成。函数是C源程序的基本模块,通过对函
数模块的调用实现特定的功能。C语言中的函数相当于其它高级语言的子程序。C语言不仅
提供了极为丰富的库函数(如 Turbo C,MS C 都提供了三百多个库函数),还允许用户建立自
己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来
使用函数。可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函
数式语言。
由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清
晰,便于程序的编写、阅读、调试。
在C语言中可从不同的角度对函数分类。

  1. 从函数定义的角度看,函数可分为库函数和用户定义函数两种。
  1. 库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序
    前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用
    到 printf、scanf、getchar、putchar、gets、puts、strcat 等函数均属此类。
  2. 用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定
    义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能
    使用。
  1. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为
    有返回值函数和无返回值函数两种。
  1. 有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数
    返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必
    须在函数定义和函数说明中明确返回值的类型。
  2. 无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返
    回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此
    类函数时可指定它的返回为“空类型”, 空类型的说明符为“void”。
  1. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。
  1. 无参函数:函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之
    间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函
    数值。
  2. 有参函数:也称为带参函数。在函数定义及函数说明时都有参数,称为形式参数(简
    称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。进行函
    数调用时,主调函数将把实参的值传送给形参,供被调函数使用。
  1. C语言提供了极为丰富的库函数,这些库函数又可从功能角度作以下分类。
  1. 字符类型分类函数:用于对字符按 ASCII 码分类:字母,数字,控制字符,分隔符,
    大小写字母等。
  2. 转换函数:用于字符或字符串的转换;在字符量和各类数字量(整型,实型等)之间进行转换;在大、小写之间进行转换。
  3. 目录路径函数:用于文件目录和路径操作。
  4. 诊断函数:用于内部错误检测。
  5. 图形函数:用于屏幕管理和各种图形功能。
  6. 输入输出函数:用于完成输入输出功能。
  7. 接口函数:用于与 DOS,BIOS 和硬件的接口。
  8. 字符串函数:用于字符串操作和处理。
  9. 内存管理函数:用于内存管理。
  10. 数学函数:用于数学函数计算。
  11. 日期和时间函数:用于日期,时间转换操作。
  12. 进程控制函数:用于进程管理和控制。
  13. 其它函数:用于其它各种功能。
    以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需
    要一个较长的学习过程。应首先掌握一些最基本、最常用的函数,再逐步深入。由于课时关
    系,我们只介绍了很少一部分库函数,其余部分读者可根据需要查阅有关手册。
    还应该指出的是,在C语言中,所有的函数定义,包括主函数 main 在内,都是平行的。
    也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。但是函数之
    间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。函数还可以自己调用自
    己,称为递归调用。
    main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。因此,C程序
    的执行总是从 main 函数开始,完成对其它函数的调用后再返回到 main 函数,最后由 main
    函数结束整个程序。一个C源程序必须有,也只能有一个主函数 main。

8.2 函数定义的一般形式

【例 8.1】

#include "string.h"
#include <stdio.h>int max(int a, int b)
{if (a > b)return a;elsereturn b;
}
main()
{int max(int a, int b);int x, y, z;printf("input two numbers:\n");scanf("%d%d", &x, &y);z = max(x, y);printf("maxmum=%d", z);
}

8.3 函数的参数和函数的值

8.3.1 形式参数和实际参数

【例 8.2】可以说明这个问题。

#include "string.h"
#include <stdio.h>main()
{int n;printf("input number\n");scanf("%d", &n);s(n);printf("n=%d\n", n);
}
int s(int n)
{int i;for (i = n - 1; i >= 1; i--)n = n + i;printf("n=%d\n", n);
}

8.3.2 函数的返回值

8.4 函数的调用

8.4.1 函数调用的一般形式

8.4.2 函数调用的方式

【例 8.3】

#include <stdio.h>main()
{int i = 8;printf("%d\n%d\n%d\n%d\n", ++i, --i, i++, i--);
}

8.4.3 被调用函数的声明和函数原型

8.5 函数的嵌套调用

【例 8.4】计算s=(2^2)! + (3^2)!;

#include <stdio.h>long f1(int p)
{int k;long r;long f2(int);k = p * p;r = f2(k);return r;
}
long f2(int q)
{long c = 1;int i;for (i = 1; i <= q; i++)c = c * i;return c;
}
main()
{int i;long s = 0;for (i = 2; i <= 3; i++)s = s + f1(i);printf("\ns=%ld\n", s);
}

8.6 函数的递归调用

【例 8.5】用递归法计算 n!
用递归法计算 n!可用下述公式表示:
n!=1 (n=0,1)
n×(n-1)! (n>1)
按公式可编程如下:

#include <stdio.h>long ff(int n)
{long f;if (n < 0)printf("n<0,input error");else if (n == 0 || n == 1)f = 1;elsef = ff(n - 1) * n;return (f);
}
main()
{int n;long y;printf("\ninput a inteager number:\n");scanf("%d", &n);y = ff(n);printf("%d!=%ld", n, y);
}

【例 8.6】Hanoi 塔问题

一块板上有三根针,A,B,C。A 针上套有 64 个大小不等的圆盘,大的在下,小的在上。
如图 5.4 所示。要把这 64 个圆盘从 A 针移动 C 针上,每次只能移动一个圆盘,移动可以借
助 B 针进行。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。求移动的步
骤。
本题算法分析如下,设 A 上有 n 个盘子。
如果 n=1,则将圆盘从 A 直接移动到 C。
如果 n=2,则:
1.将 A 上的 n-1(等于 1)个圆盘移到 B 上;
2.再将 A 上的一个圆盘移到 C 上;
3.最后将 B 上的 n-1(等于 1)个圆盘移到 C 上。
如果 n=3,则:
A. 将 A 上的 n-1(等于 2,令其为 n)个圆盘移到 B(借助于 C),步骤如下: (1)将 A 上的 n-1(等于 1)个圆盘移到 C 上。
(2)将 A 上的一个圆盘移到 B。
(3)将 C 上的 n-1(等于 1)个圆盘移到 B。 B. 将 A 上的一个圆盘移到 C。 C. 将 B 上的 n-1(等于 2,令其为 n)个圆盘移到 C(借助 A),步骤如下:
(1)将 B 上的 n-1(等于 1)个圆盘移到 A。 (2)将 B 上的一个盘子移到 C。 (3)将 A 上的 n-1(等于 1)个圆盘移到 C。
到此,完成了三个圆盘的移动过程。
从上面分析可以看出,当 n 大于等于 2 时,移动的过程可分解为三个步骤:
第一步 把 A 上的 n-1 个圆盘移到 B 上;
第二步 把 A 上的一个圆盘移到 C 上;
第三步 把 B 上的 n-1 个圆盘移到 C 上;其中第一步和第三步是类同的。
当 n=3 时,第一步和第三步又分解为类同的三步,即把 n-1 个圆盘从一个针移到另一 个针上,这里的 n=n-1。 显然这是一个递归过程,据此算法可编程如下:

#include <stdio.h>move(int n, int x, int y, int z)
{if (n == 1)printf("%c-->%c\n", x, z);else{move(n - 1, x, z, y);printf("%c-->%c\n", x, z);move(n - 1, y, x, z);}
}
main()
{int h;printf("\ninput number:\n");scanf("%d", &h);printf("the step to moving %2d diskes:\n", h);move(h, 'a', 'b', 'c');
}

8.7 数组作为函数参数

【例 8.7】判别一个整数数组中各元素的值,若大于 0 则输出该值,若小于等于 0 则输出 0
值。编程如下:

#include <stdio.h>void nzp(int v)
{if (v > 0)printf("%d ", v);elseprintf("%d ", 0);
}
main()
{int a[5], i;printf("input 5 numbers\n");for (i = 0; i < 5; i++){scanf("%d", &a[i]);nzp(a[i]);}
}

【例 8.8】数组 a 中存放了一个学生 5 门课程的成绩,求平均成绩。

#include <stdio.h>float aver(float a[5])
{int i;float av, s = a[0];for (i = 1; i < 5; i++)s = s + a[i];av = s / 5;return av;
}
void main()
{float sco[5], av;int i;printf("\ninput 5 scores:\n");for (i = 0; i < 5; i++)scanf("%f", &sco[i]);av = aver(sco);printf("average score is %5.2f", av);
}

【例 8.9】题目同 8.7 例。改用数组名作函数参数。

#include <stdio.h>void nzp(int a[5])
{int i;printf("\nvalues of array a are:\n");for (i = 0; i < 5; i++){if (a[i] < 0)a[i] = 0;printf("%d ", a[i]);}
}
main()
{int b[5], i;printf("\ninput 5 numbers:\n");for (i = 0; i < 5; i++)scanf("%d", &b[i]);printf("initial values of array b are:\n");for (i = 0; i < 5; i++)printf("%d ", b[i]);nzp(b);printf("\nlast values of array b are:\n");for (i = 0; i < 5; i++)printf("%d ", b[i]);
}

【例 8.10】如把例 8.9 修改如下:

#include <stdio.h>void nzp(int a[8])
{int i;printf("\nvalues of array aare:\n");for (i = 0; i < 8; i++){if (a[i] < 0)a[i] = 0;printf("%d ", a[i]);}
}
main()
{int b[5], i;printf("\ninput 5 numbers:\n");for (i = 0; i < 5; i++)scanf("%d", &b[i]);printf("initial values of array b are:\n");for (i = 0; i < 5; i++)printf("%d ", b[i]);nzp(b);printf("\nlast values of array b are:\n");for (i = 0; i < 5; i++)printf("%d ", b[i]);
}

【例 8.11】

#include <stdio.h>void nzp(int a[], int n)
{int i;printf("\nvalues of array a are:\n");for (i = 0; i < n; i++){if (a[i] < 0)a[i] = 0;printf("%d ", a[i]);}
}
main()
{int b[5], i;printf("\ninput 5 numbers:\n");for (i = 0; i < 5; i++)scanf("%d", &b[i]);printf("initial values of array b are:\n");for (i = 0; i < 5; i++)printf("%d ", b[i]);nzp(b, 5);printf("\nlast values of array b are:\n");for (i = 0; i < 5; i++)printf("%d ", b[i]);
}

本程序 nzp 函数形参数组 a 没有给出长度,由 n 动态确定该长度。在 main 函数中,函
数调用语句为 nzp(b,5),其中实参 5 将赋予形参 n 作为形参数组的长度。
d. 多维数组也可以作为函数的参数。在函数定义时对形参数组可以指定每一维的
长度,也可省去第一维的长度。因此,以下写法都是合法的。
int MA(int a[3][10])

int MA(int a[][10])。

8.8 局部变量和全局变量

8.8.1 局部变量

【例 8.12】

#include <stdio.h>main()
{int i = 2, j = 3, k;k = i + j;{int k = 8;printf("%d\n", k);}printf("%d\n", k);
}

8.8.2 全局变量

【例 8.13】输入正方体的长宽高 l,w,h。求体积及三个面 xy,xz,y*z 的面积。

#include <stdio.h>int s1, s2, s3;
int vs(int a, int b, int c)
{int v;v = a * b * c;s1 = a * b;s2 = b * c;s3 = a * c;return v;
}
main()
{int v, l, w, h;printf("\ninput length,width and height\n");scanf("%d%d%d", &l, &w, &h);v = vs(l, w, h);printf("\nv=%d,s1=%d,s2=%d,s3=%d\n", v, s1, s2, s3);
}

【例 8.14】外部变量与局部变量同名。

#include <stdio.h>int a = 3, b = 5; /*a,b 为外部变量*/
max(int a, int b) /*a,b 为外部变量*/
{int c;c = a > b ? a : b;return (c);
}
main()
{int a = 8;printf("%d\n", max(a, b));
}

8.9 变量的存储类别

8.9.1 动态存储方式与静态动态存储方式

8.9.2 auto 变量

8.9.3 用 static 声明局部变量

【例 8.15】考察静态局部变量的值。

#include <stdio.h>f(int a)
{auto b = 0;static c = 3;b = b + 1;c = c + 1;return (a + b + c);
}
main()
{int a = 2, i;for (i = 0; i < 3; i++)printf("%d", f(a));
}

对静态局部变量的说明:

  1. 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间
    都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数
    调用结束后即释放。
  2. 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时
    进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
  3. 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值 0(对
    数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是
    一个不确定的值。

【例 8.16】打印 1 到 5 的阶乘值。

#include <stdio.h>int fac(int n)
{static int f = 1;f = f * n;return (f);
}
main()
{int i;for (i = 1; i <= 5; i++)printf("%d!=%d\n", i, fac(i));
}

输出结果:

1!=1
2!=2
3!=6
4!=24
5!=120

8.9.4 register 变量

【例 8.17】使用寄存器变量。

#include <stdio.h>int fac(int n)
{register int i, f = 1;for (i = 1; i <= n; i++)f = f * i return (f);
}
main()
{int i;for (i = 0; i <= 5; i++)printf("%d!=%d\n", i, fac(i));
}

输出结果

1!=1
2!=2
3!=6
4!=24
5!=120

说明:

  1. 只有局部自动变量和形式参数可以作为寄存器变量;
  2. 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
  3. 局部静态变量不能定义为寄存器变量。

8.9.5 用 extern 声明外部变量

【例 8.18】用 extern 声明外部变量,扩展程序文件中的作用域。
函数声明

// header.h
extern void printMessage(void); // 声明函数// file1.c
#include "header.h"
void printMessage() {printf("Hello, World!\n");
}// main.c
#include "header.h"
int main() {printMessage(); // 使用声明的函数return 0;
}

变量声明

// file1.c
int globalVar = 10; // 定义并初始化全局变量// file2.c
extern int globalVar; // 声明globalVar是在别处定义的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29579.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HBase学习之HBaseAPI

HBase学习之HBaseAPI: package com.shujia.base;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.*; import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.util.Bytes; import org.junit.After; import org.junit.Before…

Unity制作透明材质直接方法——6.15山大软院项目实训

之前没有在unity里面接触过材质的问题&#xff0c;一般都是在maya或这是其他建模软件里面直接得到编辑好材质的模型&#xff0c;然后将他导入Unity里面&#xff0c;然后现在碰到了需要自己在Unity制作透明材质的情况&#xff0c;所以先搜索了一下有没有现成的方法&#xff0c;很…

删除名为 `XXXX` 的 conda 环境的命令

要删除名为 musetalk 的 conda 环境,您可以使用以下命令: conda remove --name musetalk --all这个命令会删除 musetalk 环境及其中安装的所有包。 执行上述命令后,您可以使用 conda env list 命令验证该环境是否已被成功删除。 如果您想要保留该环境中安装的某些包,可以先使…

C++迈向精通:当我尝试修改虚函数表

尝试修改虚函数表 本期纯整活儿好吧&#xff01;&#xff01;&#xff01;&#xff01; 初衷 有一天我突然开始好奇虚函数表是否真的存在&#xff0c;于是我开始想是否能够从C中查看或者调用虚函数表中的内容。&#xff0c;于是有了下面的操作。 操作过程 起初我并没有思路…

使用net.sf.mpxj读取project的.mpp文件

1、导入.mpp文件 public void importMppFile(String updateType, MultipartFile multipartFile) {try (InputStream inputStream multipartFile.getInputStream()) {// 读取文件的组件MPPReader mppReader new MPPReader();// 注意&#xff0c;如果在这一步出现了读取异常&a…

神经网络模型---AlexNet

一、AlexNet 1.导入tensorflow库&#xff0c;这里给简称为tf库 import tensorflow as tf from tensorflow.keras import datasets, layers, modelsdatasets&#xff1a;是用于训练和测试机器学习模型的数据集合 layers&#xff1a;是构建神经网络模型的关键组成部分 models&a…

Golang并发控制的三种方案

Channel Channel是Go在语言层面提供的一种协程间的通信方式&#xff0c;我们可以通过在协程中向管道写入数据和在待等待的协程中读取对应协程的次数来实现并发控制。 func main() {intChan : make(chan int, 5)waitCount : 5for i : 0; i < waitCount; i {go func() {intC…

上海SNEC光伏展参展总结--安科瑞

安科瑞戴婷 Acrel-Fanny 今年的snec上海光伏展吸引了来自全球各地的光伏行业专业人士及爱好者&#xff0c;本次展会共有来自30多个国家和地区的超过2000家企业参展&#xff0c;展出的光伏产品涵盖了太阳能电池、太阳能组件、逆变器、太阳能辅助设备等众多领域。 随着近年来光…

Oracle中常用特殊字符chr值

在Oracle数据库中&#xff0c;特殊字符的chr值&#xff08;即字符与ASCII码之间的对应关系&#xff09;对于数据的处理和查询非常重要。以下是一些常见的特殊字符及其对应的chr值&#xff1a; 换行符&#xff08;LF&#xff09;&#xff1a; chr值&#xff1a;10 示例&#…

《收获,不止oracle》读书笔记一:oracle体系结构

从图中可以看出,oracle数据库是由实例和一组数据库文件组成。实例是由oracle开辟的内存区和一组后台进程组成的。

2024.6.18

Python的网络编程 网络四层 在开始前,我们需要先了解一下我们在网络通信过程中的四个层次 我们上网产生的数据都是经过协议栈一层一层的封装然后经网卡发送到网络&#xff0c;经网络发送到服务端&#xff0c;然后服务端又是一层一层的解封装拿到自己想要的数据。 我们学习的…

YOLOv8目标检测算法在地平线Bernoulli2架构BPU上高效部署参考(PTQ方案)30fps!

—— 以RDK X3为例&#xff0c;修改Head部分&#xff0c;8ms疾速Python后处理程序&#xff0c;30fps稳稳当当 本文在地平线对YOLOv8s的Backbone修改的基础上&#xff0c;提出一种在地平线Bernoulli2架构BPU上部署YOLOv8的后处理思路。使用640640分辨率&#xff0c;80类别基于C…

香橙派AIPro开机测试

资料准备&#xff1a; 香橙派官网资料下载链接&#xff1a;http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-AIpro.html 硬件准备 香橙派ai pro一个 32G sd卡&#xff0c;带出厂镜像 电源 鼠标 键盘 hdmi显示屏 测试介绍 由…

4418 android4.4 + 6818 android5.1 移植gps

网上已经有的移植的资料 目前 3399 的板子上 对已有的可运行的GPS 进行测试。 我这里的 rk3399 的板卡 , Android7 Android8 都是 可以运行 GPS 应用程序的。 使用的是 ttyS4 的节点。 问题: 我发现, 开机就删掉 ttyS4 节点,也是可以 打开应用的。并且可以正常运行。不…

【机器学习】使用Perceiver模型解决Transformer效率低下问题

1.引言 1.1.技术背景 Transformer在高维度输入上计算效率低下&#xff0c;制约了Transformer框架的应用和迭代&#xff1a; 内存消耗大&#xff1a; 当处理高维度输入&#xff0c;如长文本或高分辨率图像时&#xff0c;Transformer需要将模型参数和中间状态都保存到内存中。这…

MQTT协议与tcp/ip协议的区别

MQTT协议与TCP/IP协议在多个方面存在显著的区别。以下是对这些区别的清晰归纳和解释&#xff1a; 1.协议层次与定位&#xff1a; ① TCP/IP&#xff1a;是一个协议族&#xff0c;是互联网的基础协议套件&#xff0c;它定义了电子设备如何在网络上进行通信。TCP/IP协议由多个层…

could not be registered. A bean with that name has already been defined in file

今天在使用SpringBoot中的拦截器的功能是遇到了如下的BUG&#xff0c;为了让屏幕前的您尽快排雷&#xff0c;特此记录一下 &#xff0c;报错信息如下&#xff1a; Description:The bean noLoginInterceptor, defined in class path resource [com/zwl/crm/config/MyConfig.cla…

Dubbo的多协议支持与选择策略

Dubbo作为一款高性能、分布式服务框架&#xff0c;其设计初衷之一就是提供高度灵活性和可扩展性&#xff0c;特别是在服务通信协议方面。Dubbo支持多种协议&#xff0c;以适应不同的应用场景和需求。以下是关于Dubbo多协议支持的详细介绍及选择策略&#xff1a; 支持的协议类型…

图片怎么弄成黑白的?关于将图片改成黑白的几种方法

图片怎么弄成黑白的&#xff1f;黑白照片以其独特的艺术魅力和经典的视觉效果&#xff0c;依然在摄影和图像处理中占据重要地位。无论是为了追求怀旧的氛围&#xff0c;还是为了突出图像的构图和光影效果&#xff0c;许多人都希望将彩色图片转换成黑白图片。这不仅可以赋予图像…

通俗易懂的ChatGPT的原理简介

ChatGPT是一种基于语言模型的对话生成模型。它的原理是通过预训练和微调两个步骤来生成有意义的对话回复。 在预训练阶段&#xff0c;模型使用大量的公开文本数据集进行训练。它通过学习文本中的上下文关联和语言规律来构建一个深层神经网络模型。这个模型能够预测给定上文的下…