(转)Visual C++教程(六)指针

 

Visual C++教程(六)指针
2008-08-03 21:53

今天更新指针,指针最为C++最特别、重要的地方,但也是最难学习的地方,从这一章之后涉及的内容都会比较生僻了。熟练运用指针是每个C/C++程序员所最应具备的基本功,其实只要勤加练习,并没有什么无法攻破的城墙。

===============华丽的分割线===============

学习内容:
·掌握C++的指针声明;
·掌握取得变量的指针;
·通过取值获得指针的值;
·掌握数组指针;
·掌握函数的传值和传址参数;
·了解空指针概念。

===============华丽的分割线===============

基础概念:
在讲解学习内容之前,首先通俗地解释下指针的概念(很多书上也会这么说。。),指针(Pointer)在平常生活中很常见,比如钟表的指针、仪器的指针等,指针能够指向并通知某种我们需要的事物,如时间、数据等。C++中的指针也比较类似,C++中指针以变量的形式进行体现,这个变量仅仅储存的是一个十六进制的内存地址,而这个内存地址所储存的恰恰就是指针所指向的变量,指针的主要功能是指向并体现它指向的变量,也就是说人们并不是需要指针本身,而真正的价值在于它所指向的变量,在某些时候我们无法引用变量本体,但如果在之前设立一个指针,再用指针进行访问,那么本体要读要写要杀要剐就依你的了(有点像是奸细)。

指针声明:
在使用一个指针前,我们先必须声明创建它,声明指针与声明普通变量有一些不同,在声明指针的时候,变量名前面需要加一个*(星号),而且变量类型也是有讲究的,如果是int型的指针变量,那么它就只能指向int型的变量。如果类型不吻合,很可能会发生内存读写错误。如下例,声明的是一个int型的指针变量:
int *pointer;

取得指针:
指针声明完毕后,我们就需要派遣任务给它了,指定它需要指向的变量,我们在这里使用一个特殊的运算符&。如:
int a=10;
int *pointer=&a;

“&变量名”一般用于取得指定变量的地址,然后将这个地址赋值给指针变量,任务便指派完毕,pointer指针现在正式指向变量a了。指针可以随时改变它指向的变量,如:
int a=10;
int b=20;
int *pointer=&a;
pointer=&b;

首先pointer指向了变量a,然后马上又指向了变量b,于是pointer便开始指代b,而不再是a。

===============华丽的分割线===============

操作指针:
在正式完成了任务指派后,我们就可以开始操纵指针来达到控制本体的目的了,在C++中,通过*(星号)运算符来达到控制本体的目的。这个取值的*与声明指针变量的*是两回事,不要搞混了。*运算符的目的就是求出指针对应变量的值,和&运算符的目的正好相反,如:
int a=10;
int *pointer=&a;
printf("%d",*pointer);

输出的结果就是10。又如:
int a=10;
int *pointer=&a;
*pointer++;
printf("%d",*pointer);

输出结果就是11了。
在第一个例子中,使用*pointer来输出变量a的值,因为加上星号,pointer指针代表的就是它的本体了,在第二个例子中可以看到,通过控制本体还可以执行赋值操作,也就是说,对指针使用*运算符,可以控制本体进行任何读写操作。在这里请注意,*毕竟只是运算符,也会受优先级的影响,因此注意书写顺序和括号的使用,如++*a和*++a的结果就会完全不同。

===============华丽的分割线===============

数组指针:
指针在数组中也有很大的作用,可以用指针对指针中的元素进行访问,比如:
int a[5]={1,2,3,4,5};
int *pointer=&a[0];

在这里,将pointer指向了数组a的第一个元素,操作这个指针也将针对a[0]。根据数组的储存原理,数组是一片连续的内存空间,以下是数组解析图,假设初始内存地址为000000,而一个int型占用4个字节(32Bit),因此内存地址的增量为4,当然,事实上一个程序的内存地址是不可能从0开始的:

从上图可以看到,a[0]的下一个内存地址将是a[1],再下一个将是a[2],直到a[4]。我们可不可以通过改变pointer来达到访问数组的另外元素呢?答案是肯定的,但不是pointer=&a[1];(虽然这样也可行),如果是这样就没有讲解的必要了。
通过直接运算指针可以达到改变指针在内存中的指向,如:
int a[5]={1,2,3,4,5};
int *pointer=&a[0];
pointer++;

这样,在一开始,pointer是指向a[0]的,但经过pointer++;之后,pointer的指向改变成了a[1],也就是pointer指向向后面移了一个内存单位,如果是pointer+=2;的话,那就是移动两个内存单位,也就是a[2]了,因此我们可以使用如下代码来遍历数组:
int a[5]={1,2,3,4,5};
int *pointer=&a[0];
for (int i=0;i<5;i++)
{
     printf("%d",*pointer);
     pointer++;
}

如果数组越界的话,指针依旧会往内存后面继续读取,但读取到的变量就不知道是什么变量了,究竟是系统变量还是用户变量,不得而知,值也可能五花八门,所以这样做没有任何意义,但也请不要对未知内存地址进行赋值操作,这样做很危险!

小提示:对指针进行操作,凡是有星号的,就是对其本体的值进行操作,通常叫做值操作,没有星号的,就是对其指向的地址变量进行操作,称为地址操作,但请看清计算式,不要将乘法与求值操作混为一谈了。

PS:假设数组int a[5]={2,4,6,8,10};以及指针int *pointer=&a[2];你能算出*(++pointer)**(pointer-2)+*(pointer++)的值吗?在执行这次运算后,*pointer会等于多少?

===============华丽的分割线===============

函数传值与传址:
我们在上一章讲解的函数的参数传递,是将变量的值传过去,在调用函数时,系统会为参数开辟新的内存空间并赋值,函数使用完毕后再收回,在函数使用过程中,我们无论如何去改变参数的值,原参数是不会有任何变化的,因为它们之间除了调用初期的赋值之外,之后是不在会有任何联系的,它们仅仅只是初始值继承了原参数的,这种参数传递方式我们称其为传值(ByValue)。
但对于某些操作时,我们要求在整个函数调用期间,参数与原参数之间始终保持联系,即只要改变参数,原参数的值也会同时改变,这里你可能已经想到了使用指针,当然,如果不使用指针我也不会放在这章讲了。。如果在参数中通过&来取到原参数的地址,将指针传过去,函数中就可以通过指针来控制原参数了,这种参数传递被称为传址(ByReference),又称引用传递,如下例:
#include "afxwin.h"

void addone(int *val)
{
     (*val)++;
}

int main()
{
     int a=5;
     addone(&a);
     printf("%d",a);
     system("pause");
     return 0;
}
这段代码中,首先声明变量a,赋值为5,然后调用addone函数,这个函数把a的地址作为参数进行传递,在函数中,参数为一个int型的指针变量val,调用后,指针val便会指向变量a,然后让指针所指向的值自加一,也就是让a自加一,因此,输出结果为6。
又如:
#include "afxwin.h"

void swap(int *pa,int *pb)
{
     int temp;
     temp=*pa;
     *pa=*pb;
     *pb=temp;
}

int main()
{
     int a=5;
     int b=10;
     swap(&a,&b);
     printf("%d%d",a,b);
     system("pause");
     return 0;
}
这段代码中,实现了一个交换变量值的功能,首先声明变量a和b,赋值分别为5和10,然后调用swap函数,分别将a和b的地址进行传递,在swap中用pa和pb两个指针储存,然后声明一个临时的变量temp,用于交换时的中转操作。函数开始执行时,temp会先等于pa的值,也就是5,然后让pa的值等于pb,此时pa的值变成10,最后让pb的值等于temp,也就是先前储存的5,因此这个函数执行结束后,*pa与*pb分别等于10和5,同时a和b也会等于10和5,输出结果也自然是10和5,从而完成了交换操作。

PS:之前说过scanf函数中,变量为什么必须带上&符号?那如果变量本身就是指针,还需要带上&符号吗?

===============华丽的分割线===============

空指针:
在声明一个指针后请务必为其指定一个变量,尚未指定变量的指针被称为空指针(Null Pointer),如果此时对空指针进行操作,很明显则会发生内存读写错误,让程序崩溃,而在VC.NET中被称为空指针异常,因此,我们应该尽量避免空指针,也就是指针证明后一定要尽快赋值,如果未能做到及时赋值的,也应该确保在赋值之前不会被调用。

===============华丽的分割线===============

这次的例子是一个排序,依次输入10个整数储存在数组中,然后将其按从小到大的顺序进行排序并输出,在循环中使用数组元素时必须使用指针引用。(本例涉及排序算法,具体请参照相关资料。)

#include "afxwin.h"

void swap(int *a,int *b)
{
     int temp;
     temp=*a;
     *a=*b;
     *b=temp;
}

int main()
{
     int a[10];
     int *p=&a[0];
     //输入元素
     for (int i=0;i<10;i++)
     {
          printf("请输入第%d个数:",i+1);
          scanf("%d",p);
          p++;
     }

     //排序
     for (int i=0;i<10;i++)
     {
          //重新指向第一个元素
          p=&a[0];
          for (int j=0;j<9;j++)
          {
               if (*p>*(p+1)) swap(p,p+1);
               p++;
          }
     }

     //重新指向第一个元素
     p=&a[0];
     //输出元素
     for (int i=0;i<10;i++)
     {
          printf("%d/n",*p);
          p++;
     }
     system("pause");
     return 0;
}

===============华丽的分割线===============

程序的执行效果如下:

===============华丽的分割线===============

下章将是字符串及其处理,又一个C++的难点。

 

http://hi.baidu.com/jadechoice/blog/item/cea5f0223d7c38f5d6cae214.html 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值