第5.5章: C 风格数组

English

概述

C 风格数组是一个固定大小的元素序列,存储在一整块连续的内存中。 数组是指针算术、数组退化以及 C 风格字符串的基础,因此 理解它们在内存中的行为至关重要。

在本章中,你将学到:

1. 声明 C 风格数组

C 风格数组通过指定元素类型与元素个数来声明:


int a[3];     // array of 3 ints
char b[10];   // array of 10 chars
double c[4];  // array of 4 doubles

大小必须在编译期已知,并且必须大于 0。


// int a[0];   // error: C-style arrays cannot have size 0

2. 初始化数组

你可以用花括号初始化来初始化数组:


int a[3]{10, 20, 30};

如果你提供的元素较少,其余元素会被零初始化:


int a[5]{1, 2};   // becomes {1, 2, 0, 0, 0}

如果省略大小,编译器会自行推导:


int a[]{10, 20, 30};   // size is 3

3. 数组在内存中是连续的

数组的所有元素在内存中彼此相邻存放。


int a[3]{10, 20, 30};

Example memory layout (addresses vary):

Address: 1000   1004   1008
Memory: [ 10 ] [ 20 ] [ 30 ]
             ↑      ↑      ↑
            a[0]  a[1]   a[2]

正是这种连续性让指针算术成为可能(详见第5.6章)。

4. 访问数组元素

使用 [] 运算符访问元素:


int a[3]{10, 20, 30};

int x = a[0];   // 10
int y = a[2];   // 30

a[1] = 99;      // a becomes {10, 99, 30}

5. 越界访问是未定义行为

C 风格数组不做边界检查。 访问数组之外的位置是未定义行为。


int a[3]{10, 20, 30};

// a[3];    // undefined behavior — out of bounds
// a[-1];   // undefined behavior

未定义行为意味着:

你不能依赖任何特定结果。

WebAssembly 说明

在未启用内存标记的 WebAssembly 上,越界数组访问通常 不会触发陷阱,使缺陷更难被发现。

6. fast_io::array 与 C 风格数组

::fast_io::array 是 C 风格数组更安全、更灵活的替代品。


#include <fast_io_dsal/array.h>

::fast_io::array<int, 0> empty;   // valid: zero-sized array

常规元素访问是带检查的:


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

int x = arr[1];   // safe: bounds checked

如果你有意需要非检查访问(用于性能关键的代码), ::fast_io::array 提供了:


int x = arr.index_unchecked(1);   // no bounds checking

对大多数代码,请优先使用带边界检查的 arr[i]。

7. 作为默认容器的 fast_io::vector

对大多数动态元素序列,请优先使用 ::fast_io::vector。 它自己管理大小与容量,并且同样支持带边界检查的访问。


#include <fast_io_dsal/vector.h>

::fast_io::vector<int> vec;
vec.push_back(10);
vec.push_back(20);

int x = vec[0];   // bounds checked

当你为性能关键的代码需要非检查访问时, ::fast_io::vector 也提供了:


int x = vec.index_unchecked(0);   // no bounds checking

对大多数代码,请优先使用带边界检查的 vec[i],仅在仔细审查过的 热点路径中使用 index_unchecked()。

8. 数组与指针相关但并不相同

数组与指针关系密切,但它们并不相同。

数组:

指针:

我们将在第5.7章:数组退化中详细探讨数组与指针的关系。

9. 获取数组元素的地址

你可以用 ::std::addressof 获取任何元素的地址:


#include <memory>

int a[3]{10, 20, 30};

int *p0 = ::std::addressof(a[0]);
int *p1 = ::std::addressof(a[1]);
int *p2 = ::std::addressof(a[2]);

Addresses (example):

p0 = 1000
p1 = 1004
p2 = 1008

正是这种布局让指针算术有意义(下一章)。

10. 数组不能被拷贝或赋值

C 风格数组不能用 = 拷贝或赋值。


int a[3]{1, 2, 3};
int b[3];

// b = a;   // error: arrays cannot be assigned

拷贝数组需要手工逐元素拷贝,或者使用 memcpy (详见第5.14章)。

要点