第5.12章: 指针与 vector、array 和 string

English

概述

现代 C++ 容器在原始内存之上提供了安全、便利的抽象。 然而,三大连续容器—— ::fast_io::vector、::fast_io::array 和 ::fast_io::string——在需要时都会暴露原始指针。

在本章中,你将学到:

1. ::fast_io::vector 与指针

::fast_io::vector<T> 把元素存储在一块连续的内存中。 这意味着:


::fast_io::vector<int> v{1, 2, 3, 4};

int *p = v.data();            // pointer to v[0]
int *q = p + 2;               // points to v[2]

int a = v.index_unchecked(1); // unchecked v[1]
int b = v.front_unchecked();  // unchecked v[0]
int c = v.back_unchecked();   // unchecked v[3]

⚠️ 非检查访问从不执行边界检查。

注意:fast_io 中的边界检查通常极其廉价。 当检测到越界访问时,fast_io 会立刻调用 fast_terminate(),既不打印诊断信息,也不 执行昂贵的错误处理。这让带检查的访问既快速又能提供安全性。

2. ::fast_io::array 与指针

::fast_io::array<T, N> 是固定大小的容器,行为 类似更安全的 C 风格数组。

它提供:


::fast_io::array<int, 3> a{10, 20, 30};

int *p = a.data();            // pointer to a[0]
int *q = p + 1;               // points to a[1]

int x = a.index_unchecked(2); // unchecked a[2]
int y = a.front_unchecked();  // unchecked a[0]
int z = a.back_unchecked();   // unchecked a[2]

⚠️ 非检查函数从不执行边界检查。

注意:fast_io 中的边界检查极其廉价, 因为失败时唯一采取的动作就是 fast_terminate()。

与 C 风格数组不同,::fast_io::array 允许大小为 0。

3. ::fast_io::string 与指针

::fast_io::string 把字符存储在一块连续的内存中。 这意味着:


::fast_io::string s{"Hello"};

char *p = s.data();            // pointer to 'H'
char const *q = s.c_str();     // pointer to 'H', null-terminated

char a = s.index_unchecked(1); // unchecked 'e'
char b = s.front_unchecked();  // unchecked 'H'
char c = s.back_unchecked();   // unchecked 'o'

⚠️ 非检查访问不验证边界。

注意:fast_io 中的边界检查极其廉价, 因为失败时会立刻调用 fast_terminate()。

提醒:
s.data() 可能不是以空字符结尾的。
s.c_str() 始终以空字符结尾。

4. 连续存储的保证

这三种容器都保证其元素是连续存储的:

这意味着只要你保持在边界之内,指针算术就是有效的。


int *p = v.data();
int *end = p + v.size();   // one past the end

这与 C 风格数组的行为完全相同。

5. 容器不会退化

C 风格数组在大多数表达式中会自动退化。 容器则不会。


int a[3]{1,2,3};
int *p = a;   // decay

但是:


::fast_io::vector<int> v{1,2,3};

// int *p = v;   // ❌ does not compile

你必须显式地用 .data() 请求一个指针。

6. 作用于容器存储的指针算术

一旦你从 .data() 获得了指针,指针算术的行为就 与 C 风格数组完全相同。


int *p = a.data();
int *q = p + 2;   // points to a[2]

对 ::fast_io::string 同样适用:


char *p = s.data();
char *q = p + 4;   // points to 'o'

一如既往,越界就是未定义行为。

7. ::std::to_address 与容器迭代器

::std::to_address 是从任何行为类似指针的东西 获取原始指针的通用方式。

这很重要,因为容器迭代器并不是裸指针, 尽管它们的行为像裸指针。

示例:::fast_io::vector


auto it = v.begin();          
int *p = ::std::to_address(it);

示例:::fast_io::array


auto it = a.begin();
int *p = ::std::to_address(it);

示例:::fast_io::string


auto it = s.begin();
char *p = ::std::to_address(it);

::std::to_address 是从迭代器中取出原始指针的现代、 标准认可的方式。

8. 汇总表

容器 连续? .data() 得到的指针 空字符结尾? 退化? 非检查访问?
::fast_io::vector<T> ✔ 是 T* ❌ 否 ❌ 否 ✔ index/front/back_unchecked()
::fast_io::array<T,N> ✔ 是 T* ❌ 否 ❌ 否 ✔ index/front/back_unchecked()
::fast_io::string ✔ 是 char* ✔ 通过 .c_str() ❌ 否 ✔ index/front/back_unchecked()

要点