What & How & Why

这是本文档旧的修订版!


数组、Vector 与字符串

第 3 章笔记


数组

Intro

  • 数组是一种类型,由多个相同对象组成的序列
定义数组
  • 数组的类型与包含的对象类型不同:数组的类型由对象的类型和自身的长度组成

int a; //int
int b[10]; // int[10]

  • 数组的长度必须是常量表达式
    • 长度需要大于 0(负数是不行的,)
    • 长度需要能转换为 size_t(converted expression,比如浮点就不行)

int x;
std::cin >> x;
//不合法
//类型由编译器决定,但长度要在运行期才能得到
//编译器无法推断出b[x] 的类型
int b[x];
//合法
constexpr short y = 3;
int c[y];

  • C++ 标准不支持运行期数组长度;但在编译器实现中, gcc 和 clang 都可以通过去掉 –pedantic-errors tag 来支持 variable length array。但这样做是有风险的,因为该功能是 complier denpend。除此之外,某些必须在编译器完成的功能也不能使用该类类型做为参数,比如 decltype(b)
数组的初始化
  • 默认初始化:元素都会按照默认的方式来创建(根据C++ 的方式)
  • 聚合初始化
    • 编译器可以自行推断数组长度
    • 给出的元素不足,剩余元素会进行默认初始化

int b[3]; //default int
int c[3] = {1, 2, 3}; // int[3], aggregate init
int cn[] = {1,2, 3}; //int[3]
int d[3] = {1,2} // partially aggregate init, 1,2,0

  • 不能使用 auto:auto 会将 {} 视作 initialization_list
  • 不能复制数组:
    • 初始化不能通过赋值构造
    • 拷贝赋值的结果是拷贝的指向数组首元素的指针
  • 数组作为右值会退化为指针。
  • 使用指针访问数组性能更好
字符串数组的特殊性

//char[6] 简化定义写法,会自动添加 ''\0'' 表示结束
char str[] = "hello";
//char[5] 完整定义写法,但不会为末尾添加 ''\0''
char str[] = {‘h’, 'e', 'l', 'l', '0'};

数组的复杂声明

  • 指针为元素的数组

//a[3] 是有3个元素为指针的数组
int* a[3];

  • 指向数组的指针

//注意括号
//a 是指针,指向 int[3]
int (*a) [3];

  • 数组的引用

//a 是引用,指向 int[3]
int b[3];
int (&a) [3] = b;

引用不支持聚合初始化。C++ 中不支持元素为引用的数组。从概念上来说,引用不是对象。数组要求元素使对象,因此没有元素为引用的数组。

数组中的元素访问

  • 数组中的第一个元素下标为 0
  • 通过下标操作符访问:a[0]
数组访问的细节
  • C++ 中,左值的含义被修改为了 locator value,也就是带地址信息的值(而不是按等号的左右来决定)。
    • 数组是左值,但不能放在等号左边
    • 在等号右部使用(作为右值)时,数组的类型会隐式转换为指向数组头元素的指针。也就是说,指针也可以使用 [] 操作符
    • 当使用 x[y],且 x, y 都是 Built-in 类型时,x[y] 被解析为 *(x+y),也就是转换为指针,再解引用。
    • 需要注意下标越界

int a[3] = {1,2,3};
auto b = a;
//int(&)
decltype(b)
//int*
a;
//int*
b;
//int, *(b+0)
b[0]

可以使用 decltype 来判断表达式是否是左值。