文档章节

C Primer Plus 第9章 函数 9.7 指针简介

idreamo
 idreamo
发布于 2016/07/15 07:05
字数 2046
阅读 33
收藏 1

一般来讲,指针是一个其数值为地址的变量(或更一般地说是一个数据对象)。
正如char类型的变量用字符作为其数值,而int类型变量的数值是整数,指针变量的数值表示的是地址。
如果您将某个指针变量命名为ptr,就可以使用如下语句:
ptr=&pooh;    /*把pooh的地址赋给ptr*/
对于这个语句,我们称ptr指向poohptr和&pooh的区别在于前者为一变量,而后者是一个常量。当然,ptr可以指向任何地方:
ptr=%bah;    /*令ptr指向bah而不是pooh*/
这时,ptr的值是bah的地址。
要创建一个指针变量,首先需要声明其类型。假设您想把ptr声明为可以存放一个int数值的地址,就需要 使用下面介绍的新运算符。

9.7.1  间接运算符
假定ptr指向bah,如下所示:
ptr = &bah;
这时就可以使用间接(indirection)运算符*(也称作取值(dereferencing))来获取bah中存放的数值(不要把这种一元运算符和表示乘法的二元运算符*相混淆)。
val = *ptr;  /*得到ptr指向的值*/
语句ptr=&bah;以及语句val=*ptr;放在一起等同于下面的语句:
val = bah;
由此看出,使用地址运算符和间接运算符可以间接完成上述语句的功能,这也正是“间接运算符”名称的由来。

9.7.2  指针声明
pointer ptr;  /*不能这样声明一个指针*/
为什么不能这样声明?因为这对于声明一个变量为指针是不够的,还需要说明指针所指向变量的类型原因是不同的变量类型占用的存储空间大小不同,而有些指针操作需要知道变量类型所占用的存储空间。同时,程序也需要了解地址中存储的是何种类型的数据。例如,long和float两种类型的数值可能使用相同大小的存储空间,但是它们的数据存储方式完全不同。指针的声明形式如下:
int *pi;  /*pi是指向一个整数变量的指针*/
char *pc; /*pc是指向一个字符变量的指针*/
float *pf,*pg;  /*pf和pg是指向浮点变量的指针*/
类型标识符表明了被指向变量的类型,而星号(*)表示该变量为一指针。声明int *pi; 的意思是pi是一个指针,而且*pi是int类型的。
*和指针之间的空格是可选的。
pc所指向的值(*pc)是char类型的。而pc本身又是什么类型呢?我们把它描述为“指向char的指针”类型。pc的值是一个地址,在大多数系统内部,它由一个无符号整数表示。但是,这并不表示可以把指针看作是整数类型。一些处理整数的方法不能用来处理指针,反之亦然。例如,可以进行两个整数相乘,而指针则不能。因此,指针的确是一种新的数据类型,而不是整数类型。所以,正如前面提到的,ANSI C 专门为指针提供了%p输出格式 。

9.7.3  使用指针在函数间通信

在程序清单9.15中,函数interchange()使用了指针参数,我们将对该函数进行详细的讨论。

程序清单9.15 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;
}

下面我们分析程序清单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的地址而不是x的值,所以不能实现数值的交换。

同样,把y的值赋给x,需要使用下面的语句:

*u = *v;

其执行结果相当于:

x = y;

在示例程序中,我们用一个函数实现了x和y的数值交换。首先,函数使用xy的地址作为参数,这使它可以访问xy变量。通过使用指针和运算符*,函数可以获得相应存储地址的数据,从而就可以改变这些数据。

通常情况下,可以把关于变量的两类信息传递给一个函数。如果函数的调用形式如下:

funcation (x);

这是传递的是x的值。但是如果使用下面这种函数调用形式:

funcation (&x);

那么会把x的地址传递给函数。第一种调用形式要求函数定义部分必须包含一个和x具有相同数据类型的形式参数。如下所示:

int function (int num);

而第二种形式要求函数定义部分的形式参数必须是指向相应数据类型的指针:

int funcation (int *ptr);

使用函数进行数据计算等操作时,可以使用第一种形式。但是,如果需要改变调用函数中的多个变量的值时,就需要使用第二种形式。其实,使用scanf()时已经使用了第二种形式。例如,当需要为变量num读取一个数值时,可以调用函数scanf("%d",&num);该函数调用的意思是先读取一个数值,然后将其存储到通过参数获得的地址中。

尽管interchange()只使用局部变量,但是通过使用指针,该 函数可以操作main()中变量的值。

***************************************************

关于变量:变量名称、地址和数值

前面关于指针的讨论中,变量名称、地址以及数值之间的关系是其关键所在。

编写程序时,一个变量一般有两种属性:变量名和数值(当然,还有其他属性,如数据类型等,但它们与这个主题无关)。程序被编译和加载后,同一个变量在计算机中的两个属性是地址和数值变量的地址可以被看作是在计算机中变量的名称

在许多编程语言中,变量地址只由计算机处理,对于编程人员来讲完全不可见。但是在C中,可以使用运算符&对变量的地址进行操作。

&barn就表示变量barn的地址。

可以通过使用变量名获得变量的数值。

例如printf("%d\n",barn)输出的是barn的数值。

当然 ,也可以使用运算符*从地址中获取相应的数值。

对于语句pbarn = &barn;,*pbarn是存储在地址&barn中的数值。

总之,普通的变量把它的数值作为基本数值量,而通过使用运算符&将它的地址作为间接数值量。但是对于指针 来讲,地址是它的基本数值量,使用运算符*后,该地址中存储的数值是它的间接数值量。

 

© 著作权归作者所有

idreamo
粉丝 17
博文 139
码字总数 224743
作品 0
青岛
产品经理
私信 提问
C Primer Plus 第9章 函数 9.3 递归

9.3.1 递归的使用 为了具体说明,请看下面的例子。程序清单9.6中函数main()调用了函数upanddown()。我们把这次调用称为“第一级递归”。然后upanddown()调用其本身,这次调用叫做“第二级递...

idreamo
2016/07/12
62
0
C语言书籍资料汇总

我汇总出自己收藏的C语言方面的书籍资料,方便后期使用,或许你也用的到。 以下内容,有链接的都可以下载。 一、书籍 元老级别的书籍: C程序设计语言.pdf (c语言之父) C Primer plus 第5...

BjarneCpp
2017/11/06
0
0
C Primer Plus 第10章 数组和指针 编程练习答案

1、修改程序清单10.7中的程序rain,使它不使用数组下标,而是使用指针进行计算(程序中仍然需要声明并初始化数组)。 2、编写一个程序,初始化一个double数组,然后把数组内容复制到另外两个...

idreamo
2016/08/14
160
0
书单及进度(每日更新2015-7-13)

jQuery Mobile Web Development Essentials.pdf 写的很好5 Apache Cordova 3 Programming.pdf 写的比较啰嗦,不过找不到其它的书了3 Web Development with Node and Express.pdf ING(这个已经......

cyper
2014/05/14
0
7
【C++】第12章 类和动态内存分配 知识点总结

《C++ Primer Plus》第12章知识点总结 在构造函数中使用new时应注意的事项 (1)如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete (2)new和delete必须互相兼容。new...

qq_37792173
2017/11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

八、RabbitMQ的集群原理

集群架构 写在前面 RabbitMQ集群是按照低延迟环境设计的,千万不要跨越WAN或者互联网来搭建RabbitMQ集群。如果一定要在高延迟环境下使用RabbitMQ集群,可以参考使用Shovel和Federation工具。...

XuePeng77
今天
1
0
mac系统下,brew 安装mysql,用终端可以连接,navicat却连接不上?

问题: 1.报错? 2059 - Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(../Frameworks/caching_sha2_password.so, 2): image not found 2.自己通过设置,已经把密......

写bug的攻城狮
昨天
2
0
老生常谈,HashMap的死循环

问题 最近的几次面试中,我都问了是否了解HashMap在并发使用时可能发生死循环,导致cpu100%,结果让我很意外,都表示不知道有这样的问题,让我意外的是面试者的工作年限都不短。 由于HashMap...

群星纪元
昨天
5
0
拉普拉斯算子

拉普拉斯算子是二阶微分算子。 我们知道,一维离散信号一阶微分公式如下: 相应的,一维离散信号二阶微分公式如下: 由于图像有x和y两个方向,因此图像信号属于二维离散信号。其在x,y两个...

yepanl
昨天
3
0
记录"正则表达式"

详细请查看我的博客:https://blog.enjoytoshare.club/article/RegularExpression.html 1 写在前面 正则表达式(Regular Expression)在代码中常常简写为regex。正则表达式通常被用来检索、替...

wugenqiang
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部