c++系列文章(9):数组

  数组是一种类似于vector的数据结构,数组也是存放类型相同的对象的容器,但数组的大小确定不变,不能随意向数组中增加元素。

标题定义和初始化内置数组

  数组的声明形式为“a [d]”,其中a是数组的名字,d是数组的维度。维度说明了数组中元素的个数,因此必须大于0。数组中元素的个数d也属于数组类型的一部分,编译的时候维度应该是已知的,也就是说,维度必须是一个常量表达式

unsigned cnt = 42; //不是常量表达式
constexpr unsigned sz = 42; //常量表达式
int arr[10]; //含10个整数的数组
int *parr[sz]; //含42个整型指针的数组
int bad[cnt]; //错误,cnt不是常量表达式
int farr[get_size()]; //当get_size是constexpr时正确

  默认情况下,数组的元素被默认初始化。和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那默认初始化会令数组含有未定义的值。此外定义数组的时候必须指定数组的类型,不允许使用auto关键字由初始值的列表推断类型
  可以对数组的元素进行列表初始化,此时允许忽略数组的维度。如果维度比提供的初始值数量大,则用提供的初始值初始化靠前的元素,剩下的元素被初始化为默认值。

int a1[] = {0,1,2}; //a1的维度为3
int a2[5] = {0,1,2}; //等价于a2[] = {0,1,2,0,0}
string a3[3] = {"hi","hello"}; //等价于a3={"hi","hello",""}

  字符数组有一种额外的初始化形式,我们可以使用字符串字面值对此类数组初始化。当使用这种形式时,字符串字面值的结尾处还有一个空字符,这个空字符也会像字符串的其他字符一样被拷贝到字符数组中去

char a1[] = {'a','b','c'};
char a2[] = "abc"; //等价于{'a','b','c','\0'},自动添加表示字符串结束的空字符
char a3[3] = "abc"; //错误,没有空间可存放空字符

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

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

  数组能够存放大多数类型的对象,例如可以定义一个存放指针的数组。同时数组本身就是对象,因此允许定义数组的指针和数组的引用。

int *ptrs[10]; //含有10个整型指针的数组
int &refs[10]; //错误,引用不是对象
int (*pArry)[10] = &arr; // pArry指向一个含有10个整数的数组
int (&arrRef)[10] = arr; // arrRef引用一个含有10个整数的数组
int *(&array)[10] = ptrs; //array是数组的引用,该数组含有10个整型指针

  在使用数组下标的时候,通常将其定义为size_t类型,size_t是一种机器相关的无符号类型,它被设计得足够大以便能表示内存中任意对象的大小。

指针和数组

  在C++语言中,指针和数组有非常紧密的联系,使用数组的时候,编译器一般会将其转换成指针,在很多用到数组名字的地方,编译器会自动将其替换为一个指向数组首元素的指针

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

  在使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组。但当使用decltype关键字时,得到的还是数组类型

int ia[] = {0,1,2,3,4}
auto ia2(ia); //ia2是一个整型指针,指向ia的第一个元素
decltype(ia) ia3 = {0,1,2,3,4,5,6}; //ia3是一个整型数组

  指针也可以像迭代器那样遍历数组中的元素,通过数组名字或者数组中首元素的地址来得到指向首元素的指针(类似与首迭代器);通过获取数组尾元素之后的那个并不存在的元素的地址(类似于尾后迭代器)。

int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = arr;
int *end = &arr[10]; //指向arr尾元素的下一位置的指针
for(int *b = beg;b != end;++b)
	cout << *b << endl;

  尽管直接采用取地址符&能够得到尾后指针,但这种用法极易出错。C++11引入了两个名为begin和end的函数,这两个函数与容器中的两个同名成员功能类似,但是由于数组不是类类型,因此这两个函数不是成员函数。begin函数返回指向数组首元素的指针,end函数返回指向数组尾元素下一位置的指针,这两个函数定义在iterator头文件中。

int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *pbeg = begin(ia);
int *pend = end(ia);

  当对数组使用下标运算符时,实际上是对指向数组元素的指针执行下标运算,此外只要指针指向的是数组中的元素(或者数组中未元素的下一位置),都可以执行下标运算。

int ia = {0,2,4,6,8};
int *p = &ia[2];
int j = p[1]; //p[1]等价于*(p+1),即ia[3]表示的那个元素
int k = p[-2]; //p[-2]是ia[0]表示的那个元素

  标准库类型string和vector也能执行下标运算,但是他们使用的下标必须是无符号类型,而内置的下标运算符可以处理负值,当然,结果地址必须指向原来的指针所指同一数组中的元素(或者统一数组尾元素的下一位置)。

发布了19 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/hjc132/article/details/104494852