10.3 指针简介

C语言学习栏目目录

目录

1 间接运算符:

2 声明指针

3 使用指针在函数间通信

小结:函数


源码

指针?什么是指针?从根本上看,指针(pointer)是一个值为内存地址的变量(或数据对象)。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。在C语言中,指针有许多用法。本章将介绍如何把指针作为函数参数使用,以及为何要这样用。

假设一个指针变量名是ptr,可以编写如下语句:

ptr = &pooh; // 把pooh的地址赋给ptr

对于这条语句,我们说ptr“指向”pooh。ptr和&pooh的区别是ptr是变量,而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。还可以把ptr指向别处:

ptr = &bah; // 把ptr指向bah,而不是pooh

现在ptr的值是bah的地址。要创建指针变量,先要声明指针变量的类型。假设想把ptr声明为储存int类型变量地址的指针,就要使用下面介绍的新运算符。

1 间接运算符:

假设已知ptr指向bah,如下所示:

ptr = &bah;

然后使用间接运算符*(indirection operator)找出储存在bah中的值,该运算符有时也称为解引用运算符(dereferencing operator)。不要把间接运算符和二元乘法运算符(*)混淆,虽然它们使用的符号相同,但语法功能不同。

val = *ptr; // 找出ptr指向的值

语句ptr = &bah;和val = *ptr;放在一起相当于下面的语句:

val = bah;

由此可见,使用地址和间接运算符可以间接完成上面这条语句的功能,这也是“间接运算符”名称的由来。

小结:与指针相关的运算符

地址运算符:&

一般注解:

后跟一个变量名时,&给出该变量的地址。

示例:

&nurse表示变量nurse的地址。

地址运算符:*

一般注解:

后跟一个指针名或地址时,*给出储存在指针指向地址上的值。

示例:

nurse = 22;
ptr = &nurse; // 指向nurse的指针
val = *ptr; // 把ptr指向的地址上的值赋给val

执行以上3条语句的最终结果是把22赋给val。

2 声明指针

相信读者已经很熟悉如何声明int类型和其他基本类型的变量,那么如何声明指针变量?你也许认为是这样声明:

pointer ptr; // 不能这样声明指针

为什么不能这样声明?因为声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道储存在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们储存数字却大相径庭。下面是一些指针的声明示例:

int * pi;  // pi是指向int类型变量的指针
char * pc;    // pc是指向char类型变量的指针
float * pf, * pg; // pf、pg都是指向float类型变量的指针

类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。int  *  pi;声明的意思是pi是一个指针,*pi是int类型(见下图)。

 

*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。

pc指向的值(*pc)是char类型。pc本身是什么类型?我们描述它的类型是“指向char类型的指针”。pc 的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示。但是,不要把指针认为是整数类型。一些处理整数的操作不能用来处理指针,反之亦然。例如,可以把两个整数相乘,但是不能把两个指针相乘。所以,指针实际上是一个新类型,不是整数类型。因此,如前所述,ANSI C专门为指针提供了%p格式的转换说明。

3 使用指针在函数间通信

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

/************************************************************************
功能: 使用指针解决交换函数的问题                                                                     
/************************************************************************/

#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);

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

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的值。函数要交换的是x和y的值,而不是它们的地址。与此类似,把y的值赋给x,要使用下面的语句:

*u = *v;

这条语句相当于:

x = y;

我们总结一下该程序示例做了什么。我们需要一个函数交换x和y的值。把x和y的地址传递给函数,我们让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()中变量的值。

变量:名称、地址和值

通过前面的讨论发现,变量的名称、地址和变量的值之间关系密切。我们来进一步分析。编写程序时,可以认为变量有两个属性:名称和值(还有其他性质,如类型,暂不讨论)。计算机编译和加载程序后,认为变量也有两个属性:地址和值。地址就是变量在计算机内部的名称。在许多语言中,地址都归计算机管,对程序员隐藏。然而在 C 中,可以通过&运算符访问地址,通过*运算符获得地址上的值。例如,&barn表示变量barn的地址,使用函数名即可获得变量的数值。例如,printf("%d\n",barn)打印barn的值,使用*运算符即可获得储存在地址上的值。如果pbarn=&barn;,那么*pbarn表示的是储存在&barn地址上的值。

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

虽然打印地址可以满足读者好奇心,但是这并不是&运算符的主要用途。更重要的是使用&、*和指针可以操纵地址和地址上的内容,

小结:函数

形式:

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

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

函数体

形参声明列表是用逗号分隔的一系列变量声明。除形参变量外,函数的

其他变量均在函数体的花括号之内声明。

示例:

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

传递值:

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

c = diff(a,b);

把5和2分别传递给变量x和y。5和2称为实际参数(简称实参),diff()函数定义中的变量x和y称为形式参数(简称形参)。使用关键字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类型的值
}

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值