Loading

「学习笔记」指针的基础入门

为啥会突然学这个呢?

因为长链剖分优化 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; // 3
	cout << *p << endl; // 3
	cout << &a << endl; // 0xe71e7ff82c
	cout << 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 = &pi;
	cout << i << endl; // 30
	cout << *pi << endl; // 30
	cout << **ppi << endl; // 30
}

指针的偏移、操作

数组是一块连续的存储空间。而在 C++ 中,直接使用数组名,得到的是数组的起始地址,因此,我们在 scanf 输入字符数组时不需要加 &,我们常用 [] 运算符来访问数组中某一指定偏移量处的元素。比如 a[3] 或者 p[4]。这种写法和对指针进行运算后再引用是等价的,即 p[4]*(p + 4) 是等价的两种写法。

STL 中的指针

在我们常用的 STL 容器,像 vectorset 等,还有一些 STL 函数,像 lower_boundupper_boundfind() 等等,我们都可以见到指针的身影,像 vector 的首指针 begin(),尾指针 end()find() 函数最后返回的类型是指针,等等。

指针定义需要一个关键字 iterator,定义 vector 的指针如下

vector<int>::iterator it;

下面的代码片段

vector<int> v;

int main() {
    v.emplace_back(1);
    v.emplace_back(2);
    vector<int>::iterator it = v.begin();
    cout << *it << '\n';
    return 0;
}

最后输出的是 v 的首元素。

当然,C++11 以后,可以直接用 auto 在大多数情况下来代替这个关键词。

有了指针,就可以遍历一些 STL 容器了,像 setmap 这样我们无法直接用数组下标遍历的容器。

int main() {
	set<int> s;
	set<int>::iterator it = s.begin();
	s.emplace(1);
	s.emplace(2);
	for (; it != s.end(); ++ it) {
		cout << *it << '\n';
	}
}

一些指针可以转化通过减去首指针或数组名转化成数组下标,在 vector 中是减去 begin(),在数组中是减去数组名(常见于一些代码中的 lower_boundupper_bound 函数),但是,像 set 就无法实现这个转化(可能是因为你不能直接用数组下标访问 set 中的元素)。

posted @ 2023-08-16 20:14  yi_fan0305  阅读(37)  评论(0编辑  收藏  举报