当前课程知识点:高级语言程序设计 > 函数 > 7.4 函数参数的传递 > 函数参数的传递
大家好
本讲讨论主调函数和被调函数之间
是如何通过参数传递信息的
下面分4种情况讨论
函数的形参是基本类型变量
函数的形参是指针变量
函数的形参是一维数组
函数的形参是二维数组
先来看函数的形参是基本类型变量
我们想编写这样一个程序
在主函数中定义两个变量
编写子函数将主函数中这两个变量的值交换
这里我们将子函数形参定义为基本类型整型
这个任务不难
我们先定义子函数swap
形参是两个int型变量x和y
在函数体里交换了x和y的值
在main函数里
定义了两个int型变量a和b a=7 b=11
在调用这个swap函数之前
打印出这两个实参a b的值
然后调用swap函数
再调用swap函数之后
再打印输出这两个变量实参a b的值
观察一下 这两个实参的值是否进行交换
通过运行结果
发现a和b的值没有交换
那么有的人就会想
如果我们将swap函数的形参的名字改为a和b
和主函数的变量名相同
大家可以试试 会发现
主函数里变量a和b的值还是没有交换
那么基本类型的变量作为函数参数的时候
在函数调用时
无论实参和形参的名字是否相同
实参和形参在内存中
是占有不同的内存单元
主调函数只是把实参的值传递给了被调函数的形参
我们称为传值调用
形参值的变化是不会影响到实参的
如果我们希望在一个函数体内部
访问另一个函数内部的变量
是不能通过名字的
只能通过地址
也就是间接访问方式
如果希望在swap函数中
交换主函数中两个变量的值
只能将函数的形参由基本类型修改为指针类型
通过用指针类型变量作函数的形参来实现
我们将swap函数的形参
由基本类型修改为指针类型
那么在调用这个swap函数的时候
在swap函数体内部
交换的是形参指向的变量
不是两个形参之间进行交换
所以 在这两个变量前面
应该加一个间接寻址运算符*号
就表示访问这个指针变量
所指向的那个变量的内容
注意 将这个函数的形参修改为指针类型之后
我们还要将这个函数原型也做相应的修改
同时 在这个被调函数中
应该采用间接寻址的方式
来访问这两个指针变量指向的变量
在主函数里调用这个swap函数时
应该将变量a和b的地址
分别传给对应的指针类型的形参
以下两种情况能实现两数的交换吗
来看左边swap函数
第一条赋值语句
是将形参变量p1的值
也就是这个地址值 赋给temp
第二条语句
就是将形参变量p2的值赋值给p1
最后一条赋值语句
就是将temp的值赋值给形参p2
所以 在执行完这三条语句之后
我们发现形参p1和p2的值确实是互换了
但是不会影响到p1 p2指向的变量
仅仅是交换了两个形参p1 p2的值
来看右边swap函数
指针变量temp未初始化 是随机值
temp究竟指向哪一个整形变量
在这个函数当中并没有指明
指向了一个我们未知的存储单元
接下来 我们执行这条赋值语句
就是要将p1指向的那个实参变量的值
赋给temp所指向的那个存储单元
显然 这是非法的
右边swap函数
那是不能实现*p1和*p2的值互换的
也就是说我们不能借助于
一个没有初始化的指针变量
来实现两个变量的交换
现在我们再来看
用指针变量作为函数形参
还有一个好处
就是为函数提供了一种修改实参变量值的方法
如果我们需要从函数返回多个值
但用return语句
只能从被调函数返回一个值 这时
可以将函数的形参由基本类型修改为指针类型
请看任务 在主函数中定义两个变量
要求编写一个子函数
求主函数中这两个变量的和与积
要求 子函数返回值类型为void
也就是说 和与积都不能通过return 返回
先来看子函数func
我们不想让这个被调函数func
修改实参变量a和b的值
所以这里定义两个基本类型的形参x和y
用来接收main函数中传过来的a和b值
还要定义两个指针类型形参p1和p2
指针变量p1用来将a与b的和传递出去
指针变量p2用来将a与b的积传递出去
在函数func内部
将x与y的和赋给了形参p1指向的变量
将x与y的积赋给了形参p2指向的变量
我们要根据实际需要设定函数形参的数据类型
是基本类型 还是指针类型
在主函数里定义了四个变量
sum准备用来存放a与b的和
product准备用来存放a与b的积
通过调用func函数
来求出sum和product的值
通过指针我们可以在一个函数体内
访问另一个函数体的变量
这是通过变量名不能做到的
现在 我们希望定义一个函数
能对一个数组进行操作
比如说 输入这个数组所有元素的值
对这个数组所有元素进行排序
或者输出数组所有元素的值等等
定义函数时 形参是一维数组
主调函数也应该定义了一个一维数组
不能只在一方定义
主调函数的数组称为实参数组
实参数组与形参数组类型必须一致
当数组名作为函数形参时
C编译器会将形参数组名转换为指针变量
而指针变量只能接收一个地址
所以 float a 方括号中有没有数字
C编译器是忽略不计的
对形参数组的大小也不作检查
调用函数时
要将实参数组的起始地址传递给形参数组
可以这样理解
实参数组名是一个地址值
而形参数组名是被编译器解释为一个指针变量
请看实例 现在我们要编写程序
从键盘输入某班学生C语言课的成绩
这个班一共有40个人
要求分别定义三个子函数
来进行数组输入输出和求平均值
这是输入数组元素的函数InputScore
这是输出数组元素的函数OutputScore
输入输出函数没有返回值
所以函数返回值类型都为void
这是求数组平均值的函数Average
返回值类型为float
主函数中 分别调用这三个子函数
实参是数组名
后面不要方括号
数组名代表了数组的首地址
初学者通常犯这样的错误
就是在向函数传递数组的时候
在函数名的后面加上一个方括号
现在我们知道
要向这三个函数传递数组的首地址
这个原理之后
就不会犯这样的错误了
但是这三个子函数
只能对长度为40的数组进行输入输出
求平均值 怎样才能调用同一个函数
对不同长度的数组进行相同的操作
在定义子函数时
在形参表中增加一个整型变量n
用来接收主函数中实参数组实际长度
来看看改进后的代码
每个子函数定义两个形参
形参array用来接收实参数组的首地址
形参n用于接收实参数组的长度
实参score传递数组首地址
实参n传递数组长度
调用函数时
形参数组和实参数组共占用同一段内存
实参与形参的类型要一一对应
实参数组与形参数组的大小
可以一致也可以不一致
当形参被声明为一维数组时
形参列表中数组的方括号内可以为空
即使不为空 也不起作用
下面这四种书写形式是等价的
C编译器会将形参数组名转换为指针变量
对这四个函数的解读是一致的
下面来看一下
如何向函数传递二维数组
二维数组的存储方式
是和一维数组没有什么区别
但是用二维数组做参数
它的形参应该怎么写
值得注意的是
函数中的形参其实就相当于一个声明
并不产生内存分配
形参的目的就是要让编译器知道
函数参数的数据类型
向函数传递二维数组和向函数传递一维数组
都是传递这个数组的首地址
唯一不同的是向函数传递二维数组的时候
我们必须要知道二维数组的列数
也就是说 在定义函数时
如果用二维数组作形参
不能省略数组第二维的长度
这是因为我们必须知道二维数组的列数
才能从这个起始地址开始
正确的读出数组中的每一个元素
如果在形参中不说明列数
编译器将无法定位二维数组元素的位置
下面通过例5
来看看怎样定义一个函数来处理二维数组
在主函数里定义并且初始化一个二维数组
要定义一个子函数输出二维数组中所有的元素
我们分三种情况讨论
方法一 第一维的长度可以不指定
但必须指定第二维的长度
这是主函数定义的二维数组a
第一维的长度为4
第二维的长度为5
定义子函数时
形参数组第二维的数目
必须要与实参数组第二维的数目一样
在主函数中调用子函数时
第一个参数传递二维数组的首地址
第2个参数传递二维数组的行数
第3个参数传递列数 要注意
第3个参数可以小于或等于5 不能大于5
第二种方法
是将子函数的形参定义为
指向长度为5的一维数组的指针
这三种形式定义 形式是等价的
这种认识是错误的
既然一维数组作为参数相当于一个指针
那二维数组作为参数就相当于一个二级指针
如 int * *a
因为这个二级指针没有包含二维数组列的信息
第三种方法
是利用数组的顺序存储的特性
通过降维 来访问原数组
是将二维数组当一维数组来操作
a是一个一级指针
只能接收二维数组元素的地址
系统只能靠形参m和n
来划分二维数组的行数和列数
m必须小于等于5 不能大于5
在这里这种写法
是不可以写成二维数组的形式
好了本讲我们比较了下面4种情况下
函数参数的传递情况
函数的形参是基本类型变量
函数的形参是指针变量
函数的形参是一维数组
函数的形参是二维数组
谢谢大家
-1.1 计算机程序和计算机语言
-1.2 C程序的构成
--C程序的构成
-1.3 C语言编辑、编译、运行(VC++2010学习版)
-练习题
-2.1 变量的定义和使用
--变量的定义和使用
-2.2 数据类型
--数据类型简介
--整型数据
--实型数据
-2.3 格式输出和格式输入
-2.4 运算符和表达式
--运算符和表达式
-2.5 结构化程序设计入门
-练习题
-3.1 为什么要用选择结构解决问题
-3.2 如何正确表示选择结构中的条件
-3.3 用if语句及if语句嵌套实现选择结构
-3.4 用switch语句实现选择结构
-练习
-4.1 循环的引入
-4.1 循环的引入--作业
-4.2 用while语句实现循环
-4.3 用do-while语句实现循环
-4.4 用for语句实现循环
-- 用for语句实现循环
-4.4 用for语句实现循环--作业
-4.5 何时需要用循环的嵌套
-4.6 如何提前终止循环和提前结束本次循环
-习题--作业
-5.1 数组类型的引入
--数组类型的引入
-5.2 一维数组的定义和初始化
-5.3 一维数组的使用
--一维数组的使用
-5.4 二维数组的定义和初始化
-5.5 二维数组的使用
--二维数组的使用
-习题--作业
-6.1 什么是指针
--什么是指针
-6.2 指针类型及相关概念
-6.3 指针变量的定义、赋值与使用
-6.4 指针的运算
--指针的运算
-6.5 指针与一维数组
--指针与一维数组
-6.6 指针的指针
--指针的指针
-6.7 指针与二维数组
--指针与二维数组
-习题--作业
-7.1 为什么要自定义函数
-7.2 函数的定义
--函数的定义
-7.3 函数的调用
--函数的调用
-7.4 函数参数的传递
--函数参数的传递
-7.5 函数的嵌套调用和递归调用
-7.6 变量的作用域和存储类型
-习题--作业
-8.1 字符串的存储及输入输出
-8.2 系统字符串处理函数
-8.3 自定义字符串处理函数
-习题--作业
-9.1 结构体类型的定义
--结构体类型的定义
-9.2 结构体变量及指针变量的定义及使用
-9.3 结构体变量和指针变量作函数的参数
-9.4 结构体数组的定义和使用
-9.5 共用体类型和枚举类型
--共用体和枚举类型
-习题--作业
-动态内存分配--习题
-习题--作业