首页 > 技术文章 > [C++] 数组

immjc 2017-12-13 22:02 原文

数组

数组是存放类型相同的对象的容器。这些对象本身没有名字,需要通过其所在位置访问。数组的大小确定不变,不能随意向数组中添加元素。

数组是一种复合类型,声明型如a[d],a为数组名,b为数组维度(说明数组中元素的个数)。

数组初始化

默认情况下,数组的元素被默认初始化。在函数内部定义了某种内置类型的数组,那么默认初始化令数组含有未定义的值。在函数体外部定义了某种内置类型的数组,那么默认初始化令数组含有值为0。

显式初始化数组元素

const unsigned sz = 3;
int ia1[sz] = {0, 1, 2};
// sz为常量
int a2[] = {0, 1, 2};
// 数组纬度可以为空。根据数组初始值的个数在编译期间确定数组维度
int a3[5] = {0, 1, 2};
// 等价于a3[] = {0, 1, 2, 0, 0};
string a4[3] = {"Hi", "bye"};
// 等价于a4[] = {"Hi", "bye", ""};
int a5[2] = {0, 1, 2};
// 错误,初始值超过了数组的维度

 字符数组的特殊性

当用字符串字面值对字符数组进行初始化时,一定要注意字符串字面值的结尾处还有一个空字符,这个字符会像字符串的其他自负一样被拷贝到字符数组中。

char a1[] = {'C', '+', '+'};
// 列表初始化,没有空字符
char a2[] = {'C', '+', '+', '\0'};
// 显式空字符
char a3[] = "C++";
// 自动添加表示字符串结尾的空字符
const char a4[6] = "Daniel";
// 错误。因为字符串字面值末尾包含空字符,导致数组空间不够

复杂的数组声明(难点)

int *ptrs[10];
// ptrs是含有10个整型指针的数组
int (*Parray)[10] = &arr;
// Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;
// arrRef引用一个含有10个整数的数组
int *(&arry)[10] = ptrs;
// arry是数组的引用,该数组含有10个指针

 指针与数组

 使用数组的时候编译器一般会把它转换成指针

通常情况下,使用取地址符来获取指向某个对象的指针。对数组使用下标运算符得到该数组指定位置的元素。

string nums[] = {"one", "two", "three"};
string *p = nums;
string *q = &nums[0];
// p = q

使用数组类型的对象其实是使用一个指向该数组首元素的指针。

对数组的操作实际上是指针的操作:

1、使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组。

int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia);
// ia2是一个整型指针,指向ia的第一个元素
// 相当于 auto ia2(&ia[0]);

2、使用decltype关键字时返回的类型是一个数组。

decltype(ia) ia3 = {0,1,2,3,4,5,6,7};
ia3[4] = i;

指针也是迭代器

int arr[] = {0,1,2,3,4,5};
int *p = arr;
++p;
// p指向arr[1]

使用指针也能遍历数组中的元素。前提条件是获取到指向数组第一个元素的指针和指向数组尾元素的下一位置的指针。

数组中第一个元素的指针可以通过数组名或数组中首元素的地址获得

数组尾元素下一位置的指针可以通过指针指向数组尾元素的之后那个并不存在的值的地址。但是不能对这个指针进行解引用或者递增的操作

也可以通过标准库函数begin和end来获得首指针和尾后指针。

int arr[] = {0,1,2};
int *b = begin(arr), *e = end(arr);
while (b != e) {
    cout << *b << endl;
    b++;
}

指针运算

指针的运算和迭代器相同。需要注意两个指针相减的类型是ptrdiff_t,它是一个带符号类型

如果两个指针指向不相关的对象,则他们不能比较。

下标与指针

int ia[] = {0,2,4,6,8};
int i = ia[2];
// i = 4;
int *p = ia;
i = *(p + 2);
// i = 4;

int *p = &ia[2];
int j = p[1];
// j = 6;
int k = p[-2];
// k = 0;

下标运算符所用的索引值不是无符号类型,这一点与vector和string不同

用数组初始化vector

int a[] = {0,1,2,3,4,5,6};
vector<int> ivec(begin(a), end(a));
// 全部初始化

vector<int> ivec(a + 1, a + 5);
// 部分初始化

 多维数组

多维数组其实是数组的数组。

int ia[3][4] = {{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
// 两个初始化式相同

int ia[3][4] = {{0}, {4}, {9}};
// 显式初始化每行的首元素

int ix[3][4] = {0,3,6,9};
// 显示初始化第1行。其他元素初始化为0

多维数组的下标

int (&row)[4] = ia[1];
// 把row绑定到ia的第二个4元素数组上

row定义成一个含有4个整数的数组的引用,然后把他绑定到ia的第2行

使用范围for语句处理多维数组

for (auto &row : ia)
    for (auto &col : row)
        cout << col;

外层循环的控制变量声明成引用类型,这是为了避免数组被自动转成指针

如果不声明成引用类型,则外层row的类型就变成了int*,这样内层循环就不合法了。

所以要求除了最内层的循环外,其他所有循环控制变量都应该是引用类型

指针与多维数组

多维数组名转换得来的指针实际上是指向第一个内层数组的指针

int ia[3][4];
int (*p)[4] = ia;
// p指向含有4个整数的数组
p = &ia[2];
// p指向ia的尾元素

(*p)意味着p是一个指针,指针p所指的是一个维度为4的数组,数组中元素是整数

可以使用auto来避免在数组前加上指针类型

for (auto p = ia; p != ia + 3; p++) {
    for (auto q = *p; q != *p + 4; q++)
        cout << *q;
    cout << endl;
}

外层的for循环首先声明一个指针并令其指向ia的第一个内层数组。然后依次迭代直到ia的全部3行都处理完为止。

内层for循环负责输出内层数组所包含的值,它首先令指针q指向p当前所在行数组首元素的指针。第一次解引用p得到内层数组首元素的指针。

也可以使用begin和end函数

for (auto p = begin(ia); p != end(ia); p++) {
    for (auto q = begin(*p); q != end(*p); q++)
        cout << *q;
    cout << endl;
}

类型别名简化多维数组的指针

将“4个数组组成的数组”命名为int_array

using int_array = int[4];
typedef int int_array[4];

for (int_array *p = ia; p != ia + 3; p++) {
    for (int *q = *p; q != *p + 4; p++)
        cout << *q;
    cout << endl;
}

 Tips:

C++ primer 5th 

3.43 Answer

int main()
{
    int ia[3][4] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
    cout << "The first version of printing 2D array: " << endl;
    for (auto& rows : ia)
    {
        for (auto col : rows)
        {
            cout << col << " ";
        }
        cout << endl;
    }
    cout << endl;

    cout << "The second version of printing 2D array: " << endl;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            cout << ia[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;

    cout << "The thrid version of printing 2D array: " << endl;
    for (int(*p)[4] = ia; p != ia + 3; p++)
    {
        for (int *q = *p; q != *p + 4; q++)
        {
            cout << *q << " ";
        }
        cout << endl;
    }
    cout << endl;
    return 0;
}
3.43

3.44 Answer

int main()
{
    using int_array = int[4];
    int ia[3][4] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
    for (int_array* p = ia; p != ia + 3; p++)
    {
        for (int* q = *p; q != *p + 4; q++)
        {
            cout << *q << " ";
        }
        cout << endl;
    }
    return 0;
}
3,44

 

推荐阅读