委员长 发表于 2024-12-30 21:09:34

C++Primer学习笔记03-2.数组

### 数组

定义后大小固定的容器,在某些特殊的应用来说程序的运行时性能较好,但是和 vecotr 相比损失了一些灵活性。

#### 定义和初始化内置数组

数组的大小是固定不变的,因此数组的维度必须是一个常量表达式。

```cpp
unsigned cnt = 42;                        // 不是常量表达式
constexpr unsigned sz = 42;        // 常量表达式
int arr;                                // 有 10 个整型的数组
int *parr;                                // 含 42 个整型指针的数组
string bad;                        // 错误,cnt 不是常量表达式
string strs;        // get_size 为 constexpr 时正确,否则错误
```

```cpp
#include<iostream>
#include<string>

using std::cout;
using std::endl;
using std::string;

int main() {
    int num = 10;
    // 虽然 a 指针是常量,但是指向的内容不是常量,是可以更改的。
    int *const a = #
    int arr[*a]={};
    cout<<*a<<endl; // 10
    num = 20;
    cout<<*a<<endl; // 20
    cout<<sizeof(arr)/sizeof(int); // 10
}
```

```cpp
int arr; // 有默认初始化 0
int main() {
    int arr2; // 内部的数据值是随机数
    cout<<arr<<endl; // 0
    cout<<arr<<endl; // 2129529664
}
```

和 vector 一样,数组的元素应为对象,因此不存在引用的数组。

> <b>显示初始化数组元素</b>

```cpp
const unsigned sz = 3;
int ial = {0, 1, 2};       // 含有三个元素的数组
int a2[] = {0, 1, 2};               // 维度为 3 的数组
int a3 = {0, 1, 2};               // 等价于 a3={0,1,2,0,0};
string a4 = {"hi", "bye"};// 等价于 a4[]={"hi", "byte", ""};
int a5 = {0,1,2};                 // 错误,初始值过多
```

> <b>字符数组的特殊性</b>

```cpp
char a1={'C', '|'}; // 列表初始化,无空字符
char a2={'C', '|', '\0'}; // 列表初始化,含显示的空字符
char a3 = "C++";        // 自动添加表示字符串结束的空字符
const char a4 = "Daniel"; // 错误,没有空间可以放空字符!
```

> <b>不允许拷贝和赋值</b>

不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值

```cpp
int a[] = {0, 1, 2};
int a2[] = a;        // 错误,不允许使用一个数组初始化令一个数组
a2 = a;        // 错误,不能把一个数组直接赋值给另一个数组
```

```cpp
void copy(){
    int arr[] = {1,2,3,4};
    int arr2;
    // error: invalid array assignment
    arr2 = arr;
}
```

> <b>复杂数组</b>

定义存放指针的数组比较简单和直接,但是定义数组的指针或数组的引用就稍微复杂一点了

```cpp
int *ptrs;                        // ptrs 是含有 10 个整型指针的数组
int &refs = /*?*/;        // 错误,不存在引用数组
int (*Parray) = &arr;        // Parray 指向一个含有 10 个整数的数组
int (&arrRef) = arr;         // arrRef 引用一个含有 10 个整数的数组
```

由内向外的顺序可帮助我们更好地理解 Parray 的含义:首先是圆括号括起来的部分,*Parray 意味着 Parray 是个指针,接下来观察右边,可知道 Parray 是个指向大小为 10 的数组的指针,最后观察左边,知道数组中的元素是 int。这样最终的含义就明白无误了,Parray 是一个指针,它指向一个 int 数组,数组中包含10个元素。同理,(&arrRef)表示 arrRef 是一个引用,它引用的对象是一个大小为 10 的数组,数组中元素的类型是 int。

```cpp
int main() {
    int a = 10, b = 20;
    int arr[]={a, b};
           // Parray 是指向一个含有两个元素的数组的指针。
    int (*Parray) = &arr;
           // 简单说就是 Parray 指向 arr 的首地址。
    // *Parray 可以拿到 arr 的首地址
    // **Parray 可以拿到首地址的元素
    cout<<**Parray<<"\t"; // 10
    cout<<*(*Parray+1)<<"\t"; // 20
}
```

<span style="color:red">要想理解数组声明的含义,最好的办法是从数组的名字开始按照由内向外的顺序阅读。</span>

#### 访问数组元素

可以通过 `[]` 范围,也可以通过指针来访问。

```cpp
void visited(){
    int arr[] = {1,2,3,4,5,6};
    cout<<arr<<"\t";
    cout<<*(arr+1)<<"\t";
    cout<<*(arr+2)<<"\t";
}
// 1        2        3
```

还可以用增强 for 来遍历,用 begin 和 end 获取数组元素的首尾指针进行遍历。

```cpp
#include<iostream>
#include<string>

using std::cout;
using std::endl;
using std::string;
using std::begin;
using std::end;

int main() {
    int arr[] = {1,2,3,4,5,6};
    for(auto ele : arr){
      cout<<ele<<"\t";
    }
    cout<<""<<endl;
    for (auto beg = begin(arr),last = end(arr); beg!=last; beg++){
      cout<<*beg<<"\t";
    }
}
```

> <b>使用数组初始化 vector 对象</b>

```cpp
#include<iostream>
#include<string>
#include<vector>
using std::vector;
using std::cout;
using std::endl;
using std::string;
using std::begin;
using std::end;

int main(){
    int arr[]={1,2,3,4};
    vector<int> ivec(begin(arr),end(arr));
    cout<<ivec.size()<<"\t"; //4
    return 0;
}
```

### 多维数组

严格来说,C++ 语言中没有多维数组,通常所说的多维数组其实是数组的数组。

对于二维数组来说,常把第一个维度称作行,第二个维度称作列。

> <b>多维数组的初始化</b>

```cpp
int ia = {
    {0,1,2,3},// 第一行的初始值
    {4,5,6,7},        // 第二行的初始值
    {8,9,10,11}        // 第三行的初始值
};
```

其中内层嵌套着的花括号并非必需的

```cpp
int ia = {0,1,2,3};
```

如果仅仅想初始化每一行的第一个元素,通过如下的语句即可

```cpp
// 其他未列出的元素执行默认值初始化
int ia = { {0}, {4}, {8}};
```

> <b>多维数组的下标引用</b>

可以使用下标运算符来访问多维数组的元素,此时数组的每个维度对应一个下标运算符

```cpp
int ele = arr;
int (&row) = ia; // 把 row 绑定到 ia 的第二个 4 元素数组上
```

> <b>多层 for 遍历多维数组</b>

和 vector 差不多,就用 leetcode 上的题作为例子了。

```cpp
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix,
                           int target) {
      for(auto row: matrix){
            for(auto ele : row){
                if(ele == target) return true;
            }
      }
      return false;
    }
};
```

> <b>指针和多维数组</b>

当程序使用多维数组的名字时,也会自动将其转换成指向数组首元素的指针。

定义指向多维数组的指针时,千万别忘了这个多维数组实际上是数组的数组。

因为多维数组实际上是数组的数组,所以由多维数组名转换得来的指针实际上是指向第一个内层数组的指针

```cpp
int ia;         // 大小为 3 行 4 列的数组
int (*p) = ia;        // p 指向含有 4 个整数的数组
p = &ia;                        // p 指向 ia 的尾元素
```

在上述声明中,圆括号必不可少

```cpp
int *ip;                // 整型指针的数组
int (*ip);        // 指向含有 4 个整数的数组
```

也可使用 begin 和 end。
页: [1]
查看完整版本: C++Primer学习笔记03-2.数组