当前位置:嗨网首页>书籍在线阅读

34-使用指针在函数间通信

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

9.7.3 使用指针在函数间通信

我们才刚刚接触指针,指针的世界丰富多彩。本节着重介绍如何使用指针解决函数间的通信问题。请看程序清单9.15,该程序在 interchange() 函数中使用了指针参数。稍后我们将对该程序做详细分析。

程序清单9.15  swap3.c 程序

/* swap3.c -- 使用指针解决交换函数的问题 */
#include <stdio.h>
void interchange(int * u, int * v);
int main(void)
{
     int x = 5, y = 10;
     printf("Originally x = %d and y = %d.\n", x, y);
     interchange(&x, &y);    // 把地址发送给函数
     printf("Now x = %d and y = %d.\n", x, y);
     return 0;
}
void interchange(int * u, int * v)
{
     int temp;
     temp = *u;    // temp获得 u 所指向对象的值
     *u = *v;
     *v = temp;
}

该程序是否能正常运行?下面是程序的输出:

Originally x = 5 and y = 10.
Now x = 10 and y = 5.

没问题,一切正常。接下来,我们分析程序清单9.15的运行情况。首先看函数调用:

interchange(&x, &y);

该函数传递的不是 xy 的值,而是它们的地址。这意味着出现在 interchange() 原型和定义中的形式参数 uv 将把地址作为它们的值。因此,应把它们声明为指针。由于 xy 是整数,所以 uv 是指向整数的指针,其声明如下:

void interchange (int * u, int * v)

接下来,在函数体中声明了一个交换值时必需的临时变量:

int temp;

通过下面的语句把 x 的值存储在 temp 中:

temp = *u;

记住, u 的值是 &x ,所以 u 指向 x 。这意味着用* u 即可表示 x 的值,这正是我们需要的。不要写成这样:

temp = u; /* 不要这样做 */

因为这条语句赋给 temp 的是 x 的地址( u 的值就是 x 的地址),而不是 x 的值。函数要交换的是 xy 的值,而不是它们的地址。

与此类似,把 y 的值赋给 x ,要使用下面的语句:

*u = *v;

这条语句相当于:

x = y;

我们总结一下该程序示例做了什么。我们需要一个函数交换 xy 的值。把 xy 的地址传递给函数,我们让 interchange() 访问这两个变量。使用指针和*运算符,该函数可以访问存储在这些位置的值并改变它们。

可以省略ANSI C风格的函数原型中的形参名,如下所示:

void interchange(int *, int *);

一般而言,可以把变量相关的两类信息传递给函数。如果这种形式的函数调用,那么传递的是 x 的值:

function1(x);

如果下面形式的函数调用,那么传递的是 x 的地址:

function2(&x);

第1种形式要求函数定义中的形式参数必须是一个与 x 的类型相同的变量:

int function1(int num)

第2种形式要求函数定义中的形式参数必须是一个指向正确类型的指针:

int function2(int * ptr)

如果要计算或处理值,那么使用第1种形式的函数调用;如果要在被调函数中改变主调函数的变量,则使用第2种形式的函数调用。我们用过的 scanf() 函数就是这样。当程序要把一个值读入变量时(如本例中的 num ),调用的是 scanf("%d", &num)scanf() 读取一个值,然后把该值存储到指定的地址上。

对本例而言,指针让 interchange() 函数通过自己的局部变量改变 main() 中变量的值。

熟悉Pascal和Modula-2的读者应该看出第1种形式和Pascal的值参数相同,第2种形式和Pascal的变量参数类似。C++程序员可能认为,既然C和C++都使用指针变量,那么C应该也有引用变量。让他们失望了,C没有引用变量。对BASIC程序员而言,可能很难理解整个程序。如果觉得本节的内容晦涩难懂,请多做一些相关的编程练习,你会发现指针非常简单实用(见图9.6)。

57.png

图9.6 按字节寻址系统(如PC)中变量的名称、地址和值

变量:名称、地址和值

通过前面的讨论发现,变量的名称、地址和变量的值之间关系密切。我们来进一步分析。

编写程序时,可以认为变量有两个属性:名称和值(还有其他性质,如类型,暂不讨论)。计算机编译和加载程序后,认为变量也有两个属性:地址和值。地址就是变量在计算机内部的名称。

在许多语言中,地址都归计算机管,对程序员隐藏。然而在C中,可以通过 & 运算符访问地址,通过运算符获得地址上的值。例如, &barn 表示变量 barn 的地址,使用变量名即可获得变量的数值。例如, printf("%d\n", barn) 打印 barn 的值,使用运算符即可获得存储在地址上的值。如果 pbarn = &barn; ,那么* pbarn 表示的是存储在 &barn 地址上的值。

简而言之,普通变量把值作为基本量,把地址作为通过 & 运算符获得的派生量,而指针变量把地址作为基本量,把值作为通过*运算符获得的派生量。

虽然打印地址可以满足读者好奇心,但是这并不是 & 运算符的主要用途。更重要的是使用 & 、*和指针可以操纵地址和地址上的内容,如 swap3.c 程序(程序清单9.15)所示。

小结:函数

形式:

典型的ANSI C函数的定义形式为:

返回类型 名称(形参声明列表)

函数体

形参声明列表是用逗号分隔的一系列变量声明。除形参变量外,函数的其他变量均在函数体的花括号之内声明。

示例:

int diff(int x, int y) // ANSI C
{ // 函数体开始
     int z;        // 声明局部变量
     z = x - y;
     return z;    // 返回一个值
} // 函数体结束

传递值:

实参用于把值从主调函数传递给被调函数。如果变量 ab 的值分别是 52 ,那么调用:

c = diff(a,b);

把5和2分别传递给变量 xy 。5和2称为实际参数(简称实参), diff ()函数定义中的变量 xy 称为形式参数(简称形参)。使用关键字 return 把被调函数中的一个值传回主调函数。本例中, c 接受 z 的值3。被调函数一般不会改变主调函数中的变量,如果要改变,应使用指针作为参数。如果希望把更多的值传回主调函数,必须这么做。

函数的返回类型:

函数的返回类型指的是函数返回值的类型。如果返回值的类型与声明的返回类型不匹配,返回值将被转换成函数声明的返回类型。

函数签名:

函数的返回类型和形参列表构成了函数签名。因此,函数签名指定了传入函数的值的类型和函数返回值的类型。

示例:

double duff(double, int);    // 函数原型
int main(void)
{
     double q, x;
     int n;
     ...
     q = duff(x,n);        //函数调用
     ...
}
double duff(double u, int k)    //函数定义
{
     double tor;
     ...
     return tor;    //返回double类型的值
}