
函数与模块化编程
学习目标
01 函数的定义
02 函数的调用(先声明后使用)
03 函数的参数传递
04 变量类型和存储方式
05 物流管理系统输入封装案例
PART.01 函数的定义
函数
- 独立的程序段
- 执行一个具体的、明确定义的任务
- 易于编写、理解、调试和维护
- 将需要多次执行的一组指令定义为函数
函数的定义结构
c
type_specifier function_name ( arguments ) {
执行部分
}
type_specifier:返回值的数据类型function_name:有效的函数名arguments:形式参数
函数的参数
函数的参数分为形参 和实参两种,作用是实现数据传送。
- 形参出现在函数定义中,只能在该函数体内使用。发生函数调用时,调用函数把实参的值复制1份,传送给被调用函数的形参,从而实现调用函数向被调用函数的数据传送。
其它说明:
- 实参可以是常量、变量、表达式、函数等。
- 形参变量只有在被调用时,才分配内存单元;调用结束时,即刻释放所分配的内存单元。
- 实参和形参占用不同的内存单元,即使同名互不影响。
- 在被定义的函数中,必须指定形参的类型。
- 实参与形参的类型应相同或赋值兼容。
形参和实参示例代码
c
int squarer(int x);
void main(){
//实际参数
int i;
for(i =1; i <=5; i++)
printf(" %d * %d = %d\n ", i,squarer (i));
}
int squarer(int x)
{
//形式参数
int iResult = 0;
iResult = x * x;
return iResult;
}
实参向形参传值示意图:

注意:实参值a为3.5,而形参x为整型,则将实数3.5转换成整数3,然后送到形参b。字符型与整型可以互相通用。
函数的返回值
通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。例如,max(2,3)的值是3。
- 将控制权从函数传回主调程序
- 把 return 语句中的内容返回给主调程序
示例:
c
int squarer( int x )
{
int iResult = 0;
iResult = x * x;
return iResult;
}
函数的数据类型
- 在定义函数时,对函数类型的说明,应与return语句中、返回值表达式的类型一致。
- 如果返回的是一个整型值或者没有返回值,可以省略类型标识符。
- 为了避免出现不一致,最好指定数据类型。
良好的程序设计习惯:为了使程序具有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型;即使函数类型为整型,也不使用系统的缺省处理。
函数返回值说明
- 函数的返回值是通过函数中的return语句获得的。
一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句起作用。return语句后面的括弧也可以不要,如:return z;等价于return (z); - 函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。在C语言中,凡不加类型说明的函数,自动按整型处理。
- 在定义函数时指定的函数类型一般应该和return语句中的表达式类型匹配。 如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。
- 如果被调用函数中没有return,返回一个不确定值。对于不带回值的函数,应当用
void定义函数为"无类型"(或称"空类型")。此时在函数体中不得出现return语句。
PART.02 函数的调用
函数原型和函数声明
在定义之前使用函数,必须声明,否则,一些编译器会返回错误。
采用函数原型方式,对被调用函数进行说明,其一般格式如下:
函数类型 函数名(数据类型[ 参数名][, 数据类型[ 参数名2]...]);
错误示例(调用在前,无声明):
c
#include <stdio.h>
void main(){
swap();
}
void swap()
{ }
//错误:标示符未声明
正确示例(提前函数声明):
c
#include <stdio.h>
void swap();
void main(){
swap();//编译通过
}
void swap()
{}
函数调用
- 函数调用的一般形式为:
函数名([实际参数表]); - 调用函数时,函数名称必须与具有该功能的自定义函数名称完全一致。
- 实参在个数、类型和顺序与被调用函数的形参,必须一一对应和匹配。 如果类型不匹配,C编译程序将按赋值兼容的规则进行转换。
函数调用条件
一个函数中调用另一函数(即被调用函数)需要具备哪些条件:
- 首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。
- 如果使用库函数,还应该在本文件开头用
#include命令将调用有关库函数时所需用到的信息"包含"到本文件中来。 - 如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明。
文件包含
- 文件包含:是指一个源文件可以将另外一个源文件的全部内容包含进来。
- 作用:使编译系统把指定的被包含文件嵌入到带有
#include的源文件中。
文件包含示意图:

定义和声明
注意:函数的"定义"和"声明"不是一回事。
函数的定义 是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。
函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时,编译系统能正确识别函数并检查调用是否合法。(例如函数名是否正确,实参与形参的类型和个数是否一致)。
注:系统不会检查参数名。
函数调用流程:

函数调用的嵌套
函数的嵌套调用是指,在执行被调用函数时,被调用函数又调用了其它函数。
c
f2(){...}
f1(){
f2();
...//后续语句
}
main()
{
f1();
...//后续语句
}
函数的递归调用
一个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。C语言允许函数的递归调用。在递归调用中,主调函数又是被调函数。
- 直接递归
- 间接递归
为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。
直接递归:

间接递归:

利用递归求阶乘
c
#include <stdio.h>
int factorial( int iNum);
int main() {
int n=10;
printf("i=%d factorial: %d\n",n, factorial(n));//计算10的阶乘
return 0;
}
int factorial( int iNum)
{
if ( iNum <= 1 )
return 1;
else
return (iNum*factorial(iNum-1));
}
PART.03 函数的参数传递
调用一个函数时,调用函数和被调用函数之间会发生数据传递,有两种数据传递方式
- 按值传递
- 按地址传递
按值传递
- 变量的值被传递给被调函数,并且对这个值的任何改变都不会影响被传变量的原始值
- 默认情况下,函数参数都是按值传递的
- 参数值是通过临时变量传递的
- 所有操作都针对这些临时变量
数组作为函数参数
- 一维数组元素作函数实参
数组元素作为函数的实参,与用变量作实参一样,是单向传递,即"值传送"方式。
c
void swap(int x,int y);
swap(a[0],a[1]);
- 一维数组作为参数例子
c
void f(int aiNO[],int nNum)
{
aiNO[0] = 1;
aiNO[1] = 2;
nNum = 3;
}
void main()
{
int aiNO[10]={0},nNumber=5;
f(aiNO, nNumber);
printf("%d,%d,%d", aiNO[0], aiNO[1], nNumber);
}
数组作为函数形参注意点
- 实参数组名代表数组元素的首地址。而形参是用来接收从实参传递过来的数组首元素地址。
- 在被调用函数中声明形参数组的大小为10,但在实际上,指定大小是不起作用的,C编译器对形参数组大小不做检查,只是将实参数组的首地址传给形参数组。
PART.04 数据传递方式和内存存储区域
数据传递的几种方式
主调函数与被调函数进行数据传递的方式有以下几种:
- 参数传递
- 返回值
- 全局变量
- 使用全局变量不符合程序设计中要求模块间"高内聚性、低藕合性"的原则,使这些函数依赖这些全局变量,因而使得这些函数的独立性降低。
- 使用全局变量过多,会降低程序的可读性和可维护性。
局部变量和全局变量
- 局部变量
- 在函数内部声明
- 进入一个程序块时创建,退出该程序块时销毁
- 形式参数
- 在函数定义中被声明为参数
- 在函数内部像局部变量一样使用
- 全局变量(extern)
- 在所有函数以外声明
- 在程序的整个执行期间都拥有值
静态存储和动态存储
变量从变量值存在的时间(即生存期)角度分:静态存储方式和动态存储方式。
- 静态:由系统分配固定的存储空间,全局变量就存储在静态存储区。
- 动态:系统根据需要进行动态的分配存储空间。
在动态存储区存放以下数据:
- 函数的形式参数
- 自动变量(未加static声明)
- 函数调用时的现场保护和返回地址
用户区内存划分:程序区、静态存储区、动态存储区。
存储类别
内存中的存储区域包括下面几个部分:
- 程序代码区:存放函数体的二进制代码
- 静态区/全局区(static):全局变量和静态变量的存储区域
- 堆区(heap):程序员分配释放。
- 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值
存储类别是数据在内存中的存储形式,是变量的属性,使用的一般语法:
存储类别 数据类型 变量名称;
示例:
c
auto int a; //自动变量
static int b; //静态
register int i;//寄存器变量
extern int A; //引入外部变量
存储类别定义了:
- 生命周期:保留特定值的时间长度
- 可见性:程序的哪些部分将能够"看见"该变量
存储类别介绍
- 自动的(auto):auto可以省略。
- 外部的(extern)
c
int A=10;
// 在另一个c文件使用extern int A; //引入外部变量
- 静态的(static)
在静态存储区分配存储单元,在编译时赋初值,而且只赋初值一次。如果没有赋初值,系统就会自动赋0或空字符。 - 寄存器的(register)
- 对寄存器存取远高于对内存的存取
- 只有局部自动变量和形式参数可以作为寄存器变量
- 寄存器数目是有限的
静态的局部变量
static局部变量的作用域与auto变量相同static局部变量的生存期与全局变量相同
示例代码:
c
#include <stdio.h>
void Add(){
static int i=10; //初始化工作在编译时进行,且只进行一次
i++;
printf("i=%d\n",i);
}
void main() {
Add();
Add();
}
PART.05 物流管理系统界面和输入封装
需求
物流管理系统登录界面
c
用户名:admin
密码:***
物流管理系统主界面
1.添加货物
2.查询货物
3.修改货物
5.创建订单
6.跟踪订单
7.更新订单状态
8.查看所有订单
0.退出系统
请选择:
输入控制函数封装
c
/*************************
函数名:input
函数功能:输入控制函数
参数:
maxlen表示: 允许最大输入字符数
type表示: 输入的数据类型:
0 (表示无控制,任意都可以)
1(只能输入数字)
2(只能输入字母)
3 (只能输入数字和字母)
mode表示:显示明文还是密文
1表示明文,0表示密文
str表示:保存输入数据的字符串
返回值:无
*******************/
int input(char str[], int maxLen, int type, int mode);
输入函数流程图:

物流管理系统 C 语言代码汇总
运行环境:Windows,依赖
windows.h、conio.h,仅可在 Windows 下编译运行。
main.c(主程序源文件)
说明:程序入口文件,定义全局用户数组,循环调用登录函数,登录成功后进入管理员菜单界面。
c
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <conio.h>
#include "view.h"
#include "login.h"
#include "app.h"
int g_count=0;
char userName[5][10]={"admin","lili"};
char userPasswd[5][10]={"111","222"};
int count=2;
int main(int argc, char *argv[]) {
int r=0;
while(!r){
system("cls");
r=login();
}
adminVeiw();
return 0;
}
view.h(视图头文件)
说明:视图模块头文件,声明绘制窗口、光标定位、输入控制函数。
c
//# 宏定义作用:1. 符号常量 2. 代码某些简单的函数 3. 写在头文件预防重复定义
#ifndef _VIEW_H_
#define _VIEW_H_
/*
函数名:drawWin
函数功能:绘制窗口
参数:row-行,col-列
返回值:无
*/
//绘制窗口函数
void drawWin(int row,int col);
//光标定位函数
void gotoxy(int x, int y);
/*************************
函数名:input
函数功能:输入控制函数
参数:
maxlen表示: 允许最大输入字符数
type表示: 输入的数据类型:
0 (表示无控制,任意都可以)
1(只能输入数字)
2(只能输入字母)
3 (只能输入数字和字母)
mode表示:显示明文还是密文
1表示明文,0表示密文
str表示:保存输入数据的字符串
返回值:无
*******************/
int input(char str[], int maxLen, int type, int mode);
#endif
view.c(视图源文件)
说明:视图功能实现文件,包含光标定位、绘制边框窗口、带校验+密文的键盘输入函数。
c
#include <stdio.h>
#include <windows.h>
#include <conio.h>
extern int g_count;
//光标定位函数
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
/*
函数名:drawWin
函数功能:绘制窗口
参数:row-行,col-列
返回值:无
*/
//绘制窗口函数
void drawWin(int row,int col){
system("cls");
//画首行
int i=0,j=0;
printf("+");
for(j=0;j<col;j++){
printf("--");
}
printf("+\n");
//画中间行
for(i=0;i<row;i++){
printf("|");
for(j=0;j<col;j++){
printf(" ");
}
printf("|\n");
}
//画尾行
printf("+");
for(j=0;j<col;j++){
printf("--");
}
printf("+\n");
}
/*************************
函数名:input
函数功能:输入控制函数
参数:
maxlen表示: 允许最大输入字符数
type表示: 输入的数据类型:
0 (表示无控制,任意都可以)
1(只能输入数字)
2(只能输入字母)
3 (只能输入数字和字母)
mode表示:显示明文还是密文
1表示明文,0表示密文
str表示:保存输入数据的字符串
返回值:无
*******************/
void input(char str[], int maxLen, int type, int mode){
char ch=0;
while(1){
ch=getch();
if(ch=='\r'){//回车
break;
}
else if(ch>='0' && ch<='9'){//数字
if(strlen(str)<maxLen){
if(type==0 || type==1 || type==3){
if(mode==1){//明文
putchar(ch);
}else if(mode==0){//密文
putchar('*');
}
str[strlen(str)]=ch;
}
}
}
else if((ch>='a' && ch<='z')||(ch>='A' && ch<='Z')){//英文
if(strlen(str)<maxLen){
if(type==0 || type==1 || type==3){
if(mode==1){//明文
putchar(ch);
}else if(mode==0){//密文
putchar('*');
}
str[strlen(str)]=ch;
}
}
}
else if(ch=='\b'){//回删
if(strlen(str)>0){
printf("\b \b");
str[strlen(str)-1]='\0';
}
}
else{
if(strlen(str)<maxLen){
if(type==0){
if(mode==1){//明文
putchar(ch);
}else if(mode==0){//密文
putchar('*');
}
str[strlen(str)]=ch;
}
}
}
}
}
login.h(登录模块头文件)
说明:登录模块头文件,声明登录函数
login()。
c
#ifndef _LOGIN_H_
#define _LOGIN_H_
/*
函数名:login
函数功能:登录
参数:无
返回值:1-成功 0-失败
*/
int login();
#endif
login.c(登录模块源文件)
说明:登录逻辑实现,绘制登录界面,获取用户名密码,校验全局用户名密码数组,返回登录结果。
c
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <conio.h>
#include "view.h"
#include "global.h"
/*
函数名:login
函数功能:登录
参数:无
返回值:1-成功 0-失败
*/
int login(){
char name[10]={0};
char passwd[10]={0};
int i=0;
//1. 绘制窗口
drawWin(12,15);
//2. 显示窗口元素
gotoxy(5,3);
printf("物流管理系统管理界面");
gotoxy(3,5);
printf("用户名:");
gotoxy(3,7);
printf("密码:");
//3. 光标定位用户名位置,让用户输入用户名,回车
gotoxy(12,5);
input(name,8,3,1);
// puts(name);
//4. 光标定位密码输入的位置,让用户输入密码,密码显示*,按下回车
gotoxy(12,7);
input(passwd,8,3,0);
//5. 登录校验
for(int i=0;i<count;i++){
if(strcmp(name,userName[i])==0){
//判断密码
if(strcmp(passwd,userPasswd[i])==0){
gotoxy(10,9);
printf("登录成功!\n");
Sleep(1500);
return 1;
}
else{
gotoxy(10,9);
printf("密码错误!\n");
Sleep(1500);
return 0;
}
}
}
if(i==count){
gotoxy(10,9);
printf("用户名错了!");
Sleep(1500);
return 0;
}
return 0;
}
app.h(管理员菜单头文件)
说明:管理员功能头文件,声明管理员主菜单函数
adminVeiw()。
c
#ifndef _APP_H_
#define _APP_H_
void adminVeiw();
#endif
app.c(管理员菜单源文件)
说明:管理员主菜单实现,绘制功能选择界面,打印货物、订单相关菜单选项,接收用户选择输入。
c
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <conio.h>
#include "view.h"
void adminVeiw(){
char choose[2]={0};
//1. 绘制窗口
drawWin(15,15);
//2. 显示窗口元素
gotoxy(3,5);
printf("1.添加货物");
gotoxy(3,6);
printf("2.查询货物");
gotoxy(3,7);
printf("3.修改货物");
gotoxy(3,8);
printf("5.创建订单");
gotoxy(3,9);
printf("6.跟踪订单");
gotoxy(3,10);
printf("7.更新订单状态");
gotoxy(3,11);
printf("8.查看所有订单");
gotoxy(3,12);
printf("0.退出系统");
gotoxy(3,14);
printf("请选择:");
input(choose,1,1,1);
}
global.h(全局变量头文件)
说明:全局变量头文件,对 main.c 中定义的用户名、密码、用户数量做 extern 外部声明,供 login.c 使用。
c
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
extern char userName[5][10];
extern char userPasswd[5][10];
extern int count;
#endif