C++ Primer第三章⑤

C++ Primer

第三章 字符串,向量和数组

数组

数组和vector非常类似,也是用于存放类型相同的对象,不同的地方在于,数组的大小确定不变,不能随意向数组中增加元素。因为数组的大小固定,因此对某些特殊的应用来说,程序的运行时性能较好,但是相应的也损失了一些灵活性。

如果你觉得这一章看不下去,可以直接跳过,就用vector好了,真的没太大关系

如果不清楚元素的确切个数,请使用vector。

定义和初始化内置数组

值得注意的是,数组中元素的个数也是数组类型的一部分,编译的时候维度应该是已知的,也就是说,维度必须是一个常量表达式

int arr[10]; //含有十个整数的数组,默认初始化都为0
unsigned cnt = 10; //搞了半天unsigned == unsigned int,作者装什么逼。。。
constexpr unsigned f = 10; //
string good[f]; //正确
string bad[cnt]; //错误:cnt不是常量表达式

为了避免程序出现什么低级错误,好的习惯是显式初始化数组元素,下面来看看这五花八门的初始化方式:

const unsigned sz = 3;
int a1[sz] = {0, 1, 2}; //含有三个元素的数组,元素值分别为0,1,2
int a2[] = {0, 1, 2}; //与a1等价
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}; //错误:初始值过多

总结一下就是,元素不足我可以帮你补,维度不知道我可以推断,但元素多了就不行了。

注意一下烦人的小妖精,字符数组

只有在用字符串字面值初始化数组时,编译器会在最后面加一个空字符'\0'

char a1[] = {'C', '+', '+'}; //列表初始化,后面没有空字符
char a2[] = "C++"; //自动添加空字符
char a[3] = "C++"; //错误:维度不够,没办法存放空字符

数组还有个奇怪的地方是:不允许拷贝和复制,换句话说,你只能初始化它,不知道为什么,有了解的同学请赐教啊

==下面要把数组和指针分别结合,搞点事情了== 要看懂下面的1定义,记住从右到左看,有括号先看括号,是不是跟四则运算差不多啊,就是这里是从右到左。

int *ptrs[10]; //ptrs是数组,数组里面的元素类型是int *
int (*parray)[10]; //parray是指针,指向含有10个元素的数组

int &refs[10] = ptrs; //ref是数组,数组里面的元素是引用,
//难道就这么简单?不是的——>因为引用不是对象,这个定义错了
int (&arrRef)[10] = arr; //arrRef是一个引用,引用了一个含有10个整数的数组

来个难点的,只要按照上面的基本法就可以,不要慌

int *(&arry)[10] = ptrs; //首先看括号,括号里表示arry是个引用,括号看完了
//接下来从右往左看,引用的对象是一个大小为10的数组,最后看左边知道
//数组的元素类型是指针,指向的类型是int
访问数组元素
int a[3] = {1, 2, 3};
for(int i=0; i<3; ++i)
{
    cout << a[i] << endl;
}
for(auto i : a)
{
    cout << i << endl;
}
检查下标的值

与vector和string一样,数组的下标是否在合理范围之内由程序员负责检查(其实挺讨厌的),所谓合理就是下标应该大于等于0且小于数组的大小。


==既然有了个新东西数组,于是讨厌的指针又来搞事情了==

指针和数组

像其他对象一样。对数组的元素使用取地址符就能得到指向该元素的指针:

string nums[] = {"1", "2", "3"};
string *p1 = &nums[1] //p1指向2

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

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

就因为这个,接下来的代码就需要你好好看看了:

int a[] = {0, 1, 2};
auto a2(a); 
//a是数组名,所以会被转化为指针,指向0,初始化a2,也就是说a2是int *,指向a[0]
//等价于auto a2(&a[0]);
//但是,当使用decltype关键字时,上述转换不会发生
decltype(a) a3 = {3, 4, 5};
//这里decltype的参数虽然是数组名,但是它还是会返回整型数组int[3]
//注意,数组的类型包括它的维度哦,这里列表初始化的元素最多就是3个
a3[2] = 7; //修改a3数组元素的值,与a无关了哦
用指针遍历数组
int arr[] = {0, 1, 2, 3};
int *p = arr; //p指向首元素
int *e = &arr[4] //e指向尾后元素,虽然看着不舒服,但是C++也支持这么做
for(int *b = p; b != e; ++b)
{
    cout << *b << endl;
}
标准库函数begin和end

为了用起来更顺手,我们一般用这两个函数,看着也高大上点

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

注意:尾后指针不能执行解引用和递增操作

指针运算

超简单,看看代码就好了

int arr[5] = {1, 2, 3, 4, 5};
int *p1 = arr;
int *p2 = p1 + 3; //p2指向arr[3],就是4
//给前四个数都加1
while(p1 <= p2) //指针指的是同一个对象的才能比较
{
    ++(*p1);
    ++p1;
}

空指针也可以有上述运算,虽然意义不大

解引用和指针运算的交互

名字起得玄乎罢了,没什么可怕的,看懂下面代码就行:

int arr[5] = {1, 2, 3, 4, 5};
int last = *(arr + 4); //last = arr[4],arr是数组名,
也就是指向数组首元素的指针,后移四位就是arr[4]的地址,解引用就是arr[4]
下标和指针
int arr[5] = {1, 2, 3, 4, 5};
int i1 = arr[2];
//等价于下面的句子
int *p = arr;
int i2 = *(p + 2);

接下来的一种是C++也支持的运算,但是我们不推荐这么做,大家能看懂就好,这个特性只在数组中有。(vector和string都没有,它们的下标都必须是非负数)

int arr[5] = {1, 2, 3, 4, 5};
int *p = &arr[2]; //p指向arr[2]
int j = p[1]; //p[1]等价于*(p+1),就是arr[3]
int k = p[-2]; //p[-2]等价于*(p-2),就是arr[0]
全部评论
数组不能拷贝与赋值因为数组不是基本类型,除了基本类型char short int long float double外,其他任何类型都不能用=号赋值,除非重载=号操作符号
点赞 回复 分享
发布于 2018-07-16 10:34
int a[5] = {0, 1, 2}; //错误:初始值过多 总结一下就是,元素不足我可以帮你补,维度不知道我可以推断,但元素多了就不行了。 这里的5是不是过大了,是不是应该在3以下啊,求问
点赞 回复 分享
发布于 2016-12-19 21:50

相关推荐

点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务