可爱的指针(一)
基本介绍
在互联网时代,几乎人人都会使用互联网来浏览信息。自互联网2.0以后,网页也变得越来越丰富。一个网页可以包含各种形式的信息,比如文字、图片、视频等。网页及其中的图片、视频都可以通过网址来获得。比如链接https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/potw2045a.jpg
对应一张NASA的星云图。
[图片上传失败...(image-f2f3ba-1605971654846)]
给定一个链接(或者称之为地址),浏览器会根据地址向对应的服务器获取图片(内容)。指针也是类似的概念。我们可以把网络地址看成指向网络资源的指针。
在计算机内存中,每一个资源都对应了一个地址。每当我们用程序语言定义一个变量,计算机都会在内存中开辟一个特定大小的存储空间,用来存储对应变量的值。
[图片上传失败...(image-57e600-1605971654846)]
每一个变量都有三个要素:(1)变量名,(2)变量的地址,(3)变量的值。在上面的图中,变量c
的地址为0x0012FF74
,变量c
的值为76
。不难发现,变量和上面例子当中的NASA星云图是很像的。和指向星云图的指针(网址)类似,我们可以把某个变量的地址称为指向该变量的指针。
有趣的是,计算机是可以通过变量的地址(指针)访问或改变变量的内容。抛开指针,我们可以通过cout<<c<<endl;
来打印变量c
的值。在使用指针来打印变量之前,我们首先需要获得变量的指针(地址)。C++语言中,可以通过&c
获得变量c
的地址(指针)。如果我们想把&c
存放在一个变量里,我们就需要定义一个指针变量 int* pointer
(pointer
作为指针变量,它是用来存储指针的,pointer
本身作为一个变量,也有一个对应的地址,这个地址和它本身存储的地址是不同的)。pointer = &c
称作指向变量c
的指针。
int c = 76; //定义int类型变量c,并赋值76
int *pointer; //定义名字为pointer的指针变量,*表示pointer类型为指针类型
pointer = &c; //将变量c的地址赋值给指针变量pointer
这里,int *
中的int
称为指针类型的基类型,基类型可以帮助程序确定指针指向的变量在内存中所占空间的大小。
有了指针变量之后,如何通过指针变量访问指针变量指向的变量内容呢?(如何利用pointer
访问变量c
的值76
呢?)可以使用指针运算符*
实现。使用*pointer
,我们可以获得pointer
所指向的存储单元的内容。这里,pointer
所指向的存储单元的内容是变量c
(注意,内容是变量c
而不是76
,因为我们可以实现操作*pointer = 72
,其含义等于c = 72
)。
Example 1:
void main(){
int c;
int *pointer;
c = 76;
pointer = &c;
cout<<*pointer<<endl;
}
这里,cout<<*pointer<<endl;
相当于cout<<c<<endl;
所以结果打印出76
。注意,在使用指针变量时,一定要先对其进行赋值,然后才能使用指针变量访问或者改变指向变量的值。
Example 2:
void main(){
int akey = 0,b = 0;
int *p = NULL, *q = NULL;
akey = 66;
p = &akey;
q = &b;
*q = *p;
cout<<"b = "<<b<<endl;
cout<<"*q = "<<*q<<endl;
}
这个结果会打印出
b = 66
*q = 66
程序的逻辑很简单,最关键的一行就是*q = *p;
。可以把这一行理解为b = akey;
,即把akey
的值赋值给b
。
Example 3:
void main(){
int *p1, *p2, *p;
int a,b;
cin>>a>>b;
p1 = &a; p2 = &b;
if (a < b){
p = p1;
p1 = p2;
p2 = p;
}
cout<<"a = "<<a<<", b = "<<b<<endl;
cout<<"max = "<<*p1<<", min = "<<*p2<<endl;
}
这里,我们输入3 5
,会打印出
a = 3, b = 5
max = 5, min = 3
这个程序的核心逻辑在如果a < b
,则将小数a
的指针p1
和大数b
的指针p2
交换。交换后,p1
会指向大数,p2
会指向小数。(注意,只是交换了指针,并未交换指针所指向的内容。)
Discussion of *&a
定义整型变量a
:int a = 3;
,&a
代表我把它取得整型变量a
的地址;*&a
表示取地址&a
处相应的内容(变量a
);所以,*&a
等价于a
。
Discussion of &*pointer
定义整型变量a
:int a = 3;
并且定义一个指向变量a
的指针变量pointer
:int *pointer = &a;
。*pointer
等价于整型变量a
;&*pointer
等价于&a
;(*pointer)++
等价于a++
。
Discussion of iPtr++
假设iPtr
所代表的地址是0x00000100
,
若iPtr
指向一个整型元素(占4字节),则iPtr++
等于iPtr+1*4=0x00000104
;
若iPtr
指向一个实型元素(占4字节),则iPtr++
等于iPtr+1*4=0x00000104
;
若iPtr
指向一个字符元素(占1字节),则iPtr++
等于iPtr+1*1=0x00000101
。
指针与数组
指向数组元素的指针的定义、赋值、使用与基本变量完全相同。我们可以看如下的例子:
Example 4
void main(){
int a[5] = {1,2,3,4,5};
int *p = &a[3];
cout<<*p<<endl;
*p = 100;
cout<<a[3]<<endl;
}
程序的的核心在int *p = &a[3];
和*p = 100;
。这里,*p = 100;
相当于a[3] = 100;
。所以打印出:
4
100
以上是指向数组元素的指针的使用,而我们需要关注的是指向数组的指针。
在C++中,数组名代表数组首元素的地址。定义int a[10]
,则数组名a
代表数组a[10]
中第一个元素a[0]
的地址;即a
与&a[0]
等价;若有int *p
,则p = &a[0]
与p = a
等价。(注意,a
是地址常量,不能给a
赋值)
在定义int a[10]
存放10个int类型的连续空间之后,我们可以使用a + n
来获得数组a
中第n+1
个元素的地址。(a+1
是数组a[10]
中第二个元素a[1]
的地址。)同时,指向数组元素的指针可以用做下标:[]
与*
的作用相同,比如p[i]
与*(p+i)
等价。(使用*(p+i)
要注意数组下标越界的情况!)
Discussion
如果定义int a[5] = {1,2,3,4,5}; int *p;
,假设当前i=3, a[4] = 4
,则t = *p--
相当于t = a[i--]
,先做*p
运算。
-
*++p
相当于a[++i]
,先将p
自加,然后再做*
运算; -
*--p
相当于a[--i]
,先将p
自减,然后再做*
运算; -
*p++
相当于a[i++]
,先做*
运算,在做p
自加; -
*p--
相当于a[i--]
,先做*
运算,在做p
自减;
Example 5
利用指针实现数组a
的输入和输出:
int main(){
int *p, i, a[10];
p = a;
for(i = 0; i < 10; i++)
cin>>*p++;
p = a;
for(i = 0; i < 10; i++)
cout<<*p++;
return 0;
}
输入1 2 3 4 5 6 7 8 9 10
输出1 2 3 4 5 6 7 8 9 10
Example 6
void main(){
int a[5] = {1,2,3,4,5};
int *p = &a[3];
*p = 100;
cout<<*p++<<endl;
cout<<*p--<<endl;
cout<<*--p<<endl;
}
这个例子主要看*
操作和++
或者--
的优先级。*p++
表明先计算*p
,之后再p++
,此时输出100
,*p
等价于a[4]
;*p--
先输出a[4]
的值:5
,再自减,此时*p
等价于a[3]
;*--p
首先执行--p
,此时*p
等价于a[2]
,再输出a[2]
的值:3
。故最后的输出为
100
5
3
以上是指针和一维数组之间的一些问题。下一篇将会简单介绍指针和二维数组之间的一些操作。
网友评论