近来实习无聊,复习一下c++的内容,指针作为c++一个很重要的特性,涉及内容有点点绕,我尽量用自己能明白的话表达,有错误还望指正。

本文参考地址:cplusplus.com官方教程

Introduction

在c++中,变量声明之后会被存在内存中,可以通过变量名(the identifier)进行访问,这种情况下程序不需要考虑变量的物理地址。

对于一个C++程序来说,计算机的内存就像一连串的内存单元,每个单元的大小为一个字节,每个单元都有一个唯一的地址。这些单字节的内存单元的排序方式是允许大于1个字节的数据表示占据具有连续地址的内存单元。

对于每一个内存单元都有唯一的地址,并且连续的内存单元地址也是连续的。e.g. 地址为1776的内存单元在1775之后。

变量声明之后,在程序运行时,操作系统会给变量分配地址并存到相应的位置。在程序中,指针的使用是为了得到变量的地址。

Address of operator &

变量的地址可以通过这种方式获得

1
address = &a; //将变量a的地址赋值给address

假设myvar的地址为1776:

1
2
3
myvar = 25; 
foo = &myvar;
a = myvar;

img

注:foo和bar的物理地址是运行时随机分配的。

这里的foo变量就是指针

Dereference operator *

储存物理地址的变量为指针。*这个操作符可以被读作”value pointed to by”.

接上面例子

1
baz = *foo; //read as "baz equal to value pointed to by foo"

baz为25。foo 的值为 1776,*foo为1776所指向的值。

img

总结:

1
2
3
4
myvar == 25
&myvar == 1776
foo == 1776
*foo == 25

指针的声明

在声明指针时,数据类型必须是这个指针指向变量的数据类型,即与指针本身值无关。例:

1
2
3
int* number;
char* character;
double* decimals;

这里可以将 int*看作一个数据类型,即整数的指针。一个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

int main ()
{
int firstvalue = 5, secondvalue = 15;
int * p1,* p2;

p1 = &firstvalue; // p1 = address of firstvalue
p2 = &secondvalue; // p2 = address of secondvalue
*p1 = 10; // value pointed to by p1 = 10
*p2 = *p1; // value pointed to by p2 = value pointed to by p1
p1 = p2; // p1 = p2 (value of pointer is copied)
*p1 = 20; // value pointed to by p1 = 20

cout << "firstvalue is " << firstvalue << '\n';
cout << "secondvalue is " << secondvalue << '\n';
return 0;
}

注意两个指针同时声明 !==int * p1, * p2;==

指针和数组

数组的概念与指针有关,数组被隐形的转换为该数据类型的指针,工作原理是指向第一个元素的指针。与指针不同的是,数组不可以被赋值(即不能变成其他的地址),而指针可以。

栗子!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;

int main ()
{
int numbers[5];
int * p;
p = numbers; *p = 10;
p++; *p = 20;
p = &numbers[2]; *p = 30;
p = numbers + 3; *p = 40;
p = numbers; *(p+4) = 50;
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;
}

p从数组第一元素依次后移,==5种不同的给数组元素的赋值方法==

在关于数组的一章中,括号brackets([])被解释为指定数组元素的索引。那么,事实上,这些括号是一个被称为偏移(offset)操作符的引申(dereferencing)操作符。它们和*一样,对后面的变量进行去引号,但是它们也会把括号之间的数字加到被引号的地址上。例如:

1
2
a[5] = 0;
*(a+5) = 0;

这里的两种表达是一样的,不管a是数组还是指针都是相同的。如果a是数组,那么a这个名字就是指向第一个元素的指针。

数组的初始化 intialization

声明方式

1
2
3
4
5
6
7
int myvar;
int *myptr = &myvar; //可以在初始化就赋值地址
//==============================================
int myvar;
int * myptr;
myptr = &myvar;
//这两个例子是一样的。

在上面这个例子中 注意区分==myptr = &myvar==和==*myptr = &myvar== (运行第二个,会报错,不能将int* 转化为int。即*myptr是int,&myvar是地址。

指针计算

不同数据类型指针的计算

1
2
3
4
5
6
7
char *mychar;
short *myshort;
long *mylong;

++mychar;
++myshort;
++mylong;

img

==++,–和*之间的优先级关系==

++和–运算符可以用作prefix /suffix。作为前缀,增量发生在表达式执行前,而作为后缀,增量发生在表达式执行后。这里涉及到一个优先级的关系 ++优先级>*

*p++就等同于*(p++)而它所做的是增加p的值(所以它现在指向下一个元素),但由于++被用作后缀,所以整个表达式的值被评价为原来的指针所指向的值(被增量前它所指向的地址)。

四种不同的组合

1
2
3
4
*p++   // same as *(p++): increment pointer, and dereference unincremented address
*++p // same as *(++p): increment pointer, and dereference incremented address
++*p // same as ++(*p): dereference pointer, and increment the value it points to
(*p)++ // dereference pointer, and post-increment the value it points to

另一个例子

1
*p++ = *q++;

因为++的优先级比* 高,所以p和q都是递增的,但因为这两个递增运算符(++)都是作为后缀,所以在p和q都递增之前,分配给 *p的值是 *q。然后两者都被递增。所以等同于

1
2
3
*p = *q;
++p;
++q;

所以!具体应用时,为了防止混淆加括号加括号加括号!

总结

本文总结了指针的定义和比较基础的用法,指针的更多内容还请翻阅我的博客文章,感谢阅读。