为啥会突然学这个呢?
【资料图】
因为长链剖分优化 DP 的状态转移用到了指针数组,平时的 STL 使用中也经常碰到指针。
So,就去学了一下,记录一下学习的笔记。我绝对不会告诉你另一个原因是因为最近做DP做累了想来写篇博文水水时间
我们平时用 scanf
输入的时候,都会在变量名前加一个 &
,但是,字符数组除外。这个 &
其实是取地址符,什么是地址呢?(以下为我的理解)你可以把它想象成你家房子的定位,这个就算是一个地址,只不过你家定位指向的是你家房子,而我们程序中的地址指向的是它对应的变量。约等于你住进了一个变量 /dog
这也就引出了指针的意义所在,即是一个指向元素的指向标,但是请注意,指针也是数据,存放这类数据的变量就成为“指针变量”。
我们的 &
符号,则就是取出指向这个元素的指向标(指针)。
运行下面这段代码:
int main() { int a; cin >> a; cout << a << "\n"; return 0;}
你输入 \(3\),程序就输出 \(3\),这里我们输出的是 \(a\) 这个变量元素。
再运行这段代码:
int main() { int a; cin >> a; cout << &a << "\n"; return 0;}
你再次输入 \(3\),程序却输出了一个你看不懂的东西 0xe71e7ff82c
,这个就是变量 \(a\) 的存储地址,没错,已经不是元素了,这串十六进制的数字就是变量 \(a\) 的存储地址。
C++ 中,指针的定义实在变量类型的后面加个 *
,例如 int*
,double*
等等,想要获取指针指向的元素,只需要在指针变量前加一个 *
即可。
int main() {int a;int* p = &a;cout << a << endl; // 3cout << *p << endl; // 3cout << &a << endl; // 0xe71e7ff82ccout << p << endl; // 0xe71e7ff82c}
对结构体变量也是类似。如果要访问指针指向的结构中的成员,需要先对指针进行解引用,再使用 .
成员关系运算符。不过,更推荐使用运算符 ->
这一更简便的写法。
struct ThreeInt { int a; int b; int c;};int main() { ThreeInt x{1, 2, 3}, y{6, 7, 8}; ThreeInt* px = &x; (*px) = y; // x: {6,7,8} (*px).a = 4; // x: {4,7,8} px->b = 5; // x: {4,5,8}}
当然了,也有指向指针的指针,指针的指针被我们称为二级指针,当然了,也会有三级指针、四级指针……
二级指针是在变量类型后面加 **
,三级指针是在变量类型后面加 ***
。
int main() {int i = 30;int *pi = &i;int **ppi = πcout << i << endl; // 30cout << *pi << endl; // 30cout << **ppi << endl; // 30}
指针的偏移、操作数组是一块连续的存储空间。而在 C++ 中,直接使用数组名,得到的是数组的起始地址,因此,我们在 scanf
输入字符数组时不需要加 &
,我们常用 []
运算符来访问数组中某一指定偏移量处的元素。比如 a[3]
或者 p[4]
。这种写法和对指针进行运算后再引用是等价的,即 p[4]
和 *(p + 4)
是等价的两种写法。
在我们常用的 STL 容器,像 vector
,set
等,还有一些 STL 函数,像 lower_bound
,upper_bound
,find()
等等,我们都可以见到指针的身影,像 vector
的首指针 begin()
,尾指针 end()
,find()
函数最后返回的类型是指针,等等。
指针定义需要一个关键字 iterator
,定义 vector
的指针如下
vector::iterator it;
下面的代码片段
vector v;int main() { v.emplace_back(1); v.emplace_back(2); vector::iterator it = v.begin(); cout << *it << "\n"; return 0;}
最后输出的是 v
的首元素。
当然,C++11 以后,可以直接用 auto
在大多数情况下来代替这个关键词。
有了指针,就可以遍历一些 STL 容器了,像 set
、map
这样我们无法直接用数组下标遍历的容器。
int main() {set s;set::iterator it = s.begin();s.emplace(1);s.emplace(2);for (; it != s.end(); ++ it) {cout << *it << "\n";}}
一些指针可以转化通过减去首指针或数组名转化成数组下标,在 vector
中是减去 begin()
,在数组中是减去数组名(常见于一些代码中的 lower_bound
、upper_bound
函数),但是,像 set
就无法实现这个转化(可能是因为你不能直接用数组下标访问 set
中的元素)。
标签: