c++笔记_数组


一、定义和初始化内置数组

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

unsigned cnt = 42;		//不是常量表达式
constexpr unsigned sz =42;		//常量表达式
int arr[10];			//含有10个整数的数组
int *parr[sz];			//含有42个整数的数组
string bar[cnt];		//错误:cnt不是常量表达式
string strs[get_size()];		//当get_size是constexpr时正确,否则错误

定义数组的时候必指定数组的类型,不允许用auto关键字由初始值的列表推断类型。

二、显示初始化数组元素

1.对数组的元素进行列表初始化,允许忽略数组的维度。
2.在声明数组时没有指明维度,编译器会根据初始值的数量计算并推测出来。
3.指明了维度,那么初始值的总量不应该超过指定的大小。
4.如果维度比提供的初始值数量大,则用提供的初始值初始化靠前的元素,剩下的元素被初始化成默认值:

const unsigned sz =3;
int ial[sz] = {0,1,2};				//含有3个元素的数组,元素值分别是0,1,2
int a2[] = {0,1,2};					//维度是3的数组
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] ="abcefg";			//错误:没有多余的空间存放空字符,至少7个

四、不允许拷贝和赋值

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

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

五、复杂的数组声明

数组也能存放大多数类型的对象,例如,可以定义一个存放指针的数组。又因为数组本身就是对象,所以允许定义数组的指针及数组的引用。

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

六、指针和数组

使用取地址符(&)来获取指向某个对象的指针,取地址符可以用于任何对象。数组的元素的也是对象,对数组使用下标运算符得到该数组指定位置的元素。因此像其他对象一样,对数组的元素使用取地址符就能得到指向该元素的指针:

string nums[] = {"one","two","three"};		//数组的元素是string对象
string *p = &nums[0];						//p指向nums第一个元素

在很多用到数组名字的地方,编译器会自动地将其替换为一个指向数组首元素的指针

string *p2 = nums; 						//等价于p2 = &nums[0]

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

int ia[] = {0,1,2,3,4,5,6};			//ai是一个含有7个整数的数组
auto ia2(ia);						//ia2是一个整型指针,指向ia的第一个元素(0)
ia2 = ia ;							//错误:ia2是一个指针,不能用int值给指针赋值

尽管ia是由10个整数构成的数组,但当使用ia作为初始值时,编译器实际执行但初始化过程类似于下面的形式:

auto ia2(&ia[0]);

当使用decltype关键字时上述转换不会发生,decltype(ia)返回的类型是由10个整数构成的数组:

//ia3是一个含有10个整数的数组
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
ia3 = p;				//错误:不能用整型指指针给属猪赋值
ia3[4] = i;				//正确:把i的值赋给ia3的一个元素

七、指针也是迭代器

vector和string的迭代器支持的运算,数组的指针也全部支持:

//使用递增运算符将指向数组元素的指针向前移动到下一个位置上
int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;			//p指向arr的第一个元素
++p;					//p指向arr[1]

八、标准库函数begin和end

数组也可以使用begin和end函数,不过数组毕竟不是类类型,因此这两个函数不是成员函数。正确的使用形式是将数组作为它们的参:

//使用递增运算符将指向数组元素的指针向前移动到下一个位置上
#include<iterator>			//begin和end函数定义在iterator头文件中
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia);				//指向ia首元素的指针
int *last = end(ia);				//指向ia尾元素的下一位置的指针

使用bengin和end可以很容易写出一个循环并处理数组中的元素:

//找到arr数组中的一个负数
int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *pbeg = begin(arr);				//指向ia首元素的指针
int *pend = end(arr);				//指向arr尾元素的下一位置的指针

//寻找第一个负值元素,如果已经检查完全部元素则结束循环
while(pbed!= pend && *pbeg >= 0)
	++pbeg;

九、指针运算

指向数组元素的指针可以执行迭代器运算:
1.给一个指针加上(减去)某整数值,结果仍是指针。新指针指向的元素与原来的指针相比前进了(后退了)该整数值个位置:

constexpr size_t sz = 5;
int arr[sz] = {1,2,3,4,5};
int *ip = arr;				//等价于int *ip = &arr[0]
int *ip2 = ip + 4;			//ip2指向arr的尾元素arr[4]

ip加上4所得的结果仍是一个指针,该指针所指的元素与ip原来所指的元素相比前进了4个位置。

给指针加上一个整数,得到的新指针仍需指向同一个数组的其他元素,或者指向同一个数组的尾元素的下一个位置:

//正确:arr转换成指向它首元素的指针;p指向arr尾元素的下一位置
int arr[sz] = {1,2,3,4,5};
int *p = arr +sz;			//不要用解引用
int *p2 = arr + 10;			//错误:arr只有5个元素,p2的值未定义

和迭代器一样,两个指针相减的结果是它们之间的距离。参与运算的两个指针必须指向同一个数组当中的元素:

auto n = end(arr) - begin(arr);

只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一位置,就能利用关系运算符对其进行比较:

//可以按照下面的方法遍历数组中的元素
int arr[sz] = {1,2,3,4,5};
int *b = arr ,*e = arr+sz;
while(b<e){
	++b
}

如果两个指针分别指向不相关的对象,则不能比较它们

int i = 0,sz = 42;
int *p = &i, *e = &sz;
//未定义的:p和e无关,因此比较毫无意义。
while(p<e)

10、解引用和指针运算交互

指针加上一个整数所得的结果还是一个指针。假设结果指针指向了一个元素,则允许解引用该结果指针:

int ia[] = {0,2,4,6,8};			//含有5个整数的数组
int last = *(ia+4);				//正确:把last初始化成8,也就是ia[4]的值

11、下标和指针

使用数组的名字其实用的是一个指向数组首元素的指针。一个典型的例子是当数组使用下标运算符时,编译器会自动执行上述转换操作:

int ia[] = {0,2,4,6,8};			//含有5个整数的数组

ia[0]是一个使用了数组名字的表达式,对数组执行下标运算其实是对指向数组元素的指针指向下标运算:

int ia[] = {0,2,4,6,8};			//含有5个整数的数组
int i  = ia[2];				//ia转换成指向数组首元素的指针
							//ia[2]得到(ia+2)所指的元素
int *p = ia;				//p指向ia的首元素
i = *(p+2);					//等价于i=ia[2]

只要指针指向的数组中的元素(或者数组中尾元素的下一个位置),都可以执行下标运算:

int *p =&ia[2];				//p指向索引为2的元素
int j = p[1];				//p[1]等价于*(p+1),就是ia[3]表示的那个元素
int k =p[-2];				//p[-2]是ia[0]表示的那个元素

总结

数组指针->数组名就是指向数组首元素的指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值