C++ Primer学习纪录(五)数组和C语言风格的字符串

内置数据类型——数组和C风格的字符串

数组

数组是类似于标准库vector的数据结构,但性能和灵活程度上与vector有所区别。通俗来说数组的运行性能优于vector,但数组缺乏灵活性。

数组的定义和初始化

数组定义一般写为elem_type arr_name[const_expression], 其中const_expression为常量表达式,以下列举了一些数组初始化的方式。

constexpr unsigned size = 42;
int arr[10]; // 含有10个整数
int arr[size]; // 含有42个整数的数组

constexpr int get_size(){
    return 10;
}
int arr[get_size()];

数组可以使用列表初始化来显示确定数组的值。

constexpr unsigned sz = 3;
int ial[sz] = {0, 1, 2}; // 列表初始化 拷贝初始化形式
int a2[sz]{0, 1, 2}; // 列表初始化 直接初始化形式
int a3[]{0, 1, 2}; // 不指定数组大小的初始化形式
int a4[sz] = {0, 1}; // a4的前两个元素被初始化为0,1, 最后一个被初始化为0

数组返回的其实是指向数组头元素的指针, 即 int *ptr = &arr[0], 因此无法直接给数组赋值。

该语句int a2[] = arr;是错误的赋值语句; 同样int a2[10]; a2 = arr;也是行不通的。

复杂数组声明

arr[] = {0 ... 9};

int *ptrs[10] ptrs是大小为10的数组,其中的元素为int*类型(指针类型)

int (*ptrs)[10] = &arr; ptrs是指针类型,它指向了含有十个指针的数组。由于数组名实质是指针,因此ptrs是指向了指针的指针

int (&arrRef)[10] = arr; arrRef是引用类型(必须初始化), 它是一个含有10个整数元素数组的别名(arr的别名),因此代码arrRef[0]arr[0]是一样的

数组元素的访问

数组元素可以使用下标和指针来进行访问。数组的下标被定义为size_t类型,目的和size_type是一样的。其定义在cstddef文件中。

数组的下标

数组的下表和vectorstring的下标相同,依旧是[0 ~ length)的左闭右开区间。编译器不会检查下标越界,所以即使能通过编译也不一定能顺利运行。

指针和数组

对于编译器而言,数组通常被看为指针。以下定义会返回指针类型。

int num[] = {0, 1, 2, 3};
// 以下两句是一样的
int *ptr = &num[0]; // ptr指向num的第一个元素
int *ptr2 = num; // ptr2依旧指向数组的第一个元素

// 以下两句是一样的
auto ptr3(num); // ptr3依旧指向数组的第一个元素(指针类型)
auto ptr4(&num[0]); // ptr4依旧指向数组的第一个元素

但对于decltype而言,情况有点特殊,decltype直接返回了一个数组而非指针。

decltype(num) num_2 = {3, 4, 5, 6}; // num_2是一个指向4个整数的数组
cout << num_2[2];
output: 2;

指针和数组的区别:

虽然数组能够被看成指针,而且大部分时候数组被隐式转化为了指向首地址的指针类型右值,但实际上他们是有区别的。

数组在对其用sizeof&操作符时不会转化为指针类型。

对于数组名而言,数组名实际就是一块内存的地址,相当于某一块内存的别名;因此数组名是一个地址常量或常量指针(没有分配内存),其地址是无法改变的,无法使用增量运算符(++arr是不被允许的), 但可以使用*(arr + 1)的形式。

对于指针而言,除了要为数组开辟一段空间以外,还需要开辟一个额外的空间用来指向数组的首地址(即指针自身所需的空间)。对于指针而言,指针内所保存的地址是可以改变的,因此可以使用增量运算符。

指针访问数组元素

int arr[] =  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = arr; // 等同于int *p = &arr[0];
++p;
cout << *p;
output: 1;

上述代码中, int *p = arr获取了arr的首地址(当然也可以使用int *p = &arr[0];来获取),通过同样的方式,可以获取尾元素的下一位置指针;

int *end = &arr[10]

虽然arr[10]并不存在,但依旧可以通过&arr[10]来获取地址,但是千万不要对其进行解引用,即*end操作,这会得到无用数据。

C++11中引入了begin()和end()两个函数来获取数组的首地址与末尾元素后一个位置的地址。

使用前需要包含头文件 #include <iterator>

使用方法如下

#include <iterator>
int arr[] = {0, 1, 2, 3, 4};
int *pbeg = begin(arr), *bend = end(arr);
while(pbeg != bend){
    ...
    ++pbeg;
}

对于数组中元素个数的统计,可以使用

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

这里返回一个ptrdiff_t的数据类型,定义在cstddef

指针下标

对指针下标进行操作时,编译器会自动转化为指针运算的操作。

例如:

int arr[] =  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = arr;
// 以下两句是等价的
p[5] += 7;
*(p + 5) += 7;

因此有个有趣的现象,指针的下标可以是负数(小心指向无意义的内存区域)。

int arr[] =  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &arr[3];
cout << p[-2];
output: 1;

C风格的字符串

C风格的字符串用C的char类型数组来表示,并且在数组末尾添加\0用来表示字符串的结束。因此字符串实际占用了 字符串长度+1 的内存空间大小。

当结尾处没有\0或者开辟空间不足时,都会造成难以估计的错误。

例如:

char str[] = {'C', '+', '+'};
cout << strlen(str);

由于忘记添加\0strlen会一直寻找\0,直到遇到\0才会停下来。

由于数组可以近似理解为指针,因此对于两个字符串的一系列操作(拷贝、比较、连接等),无法直接使用运算符来完成,例如str1 < str2这样的比较是根本没有意义的, 这比较的是两个指针指向的内存地址的大小,而非字符串大小。因此C语言中有标准库来替我们完成这些操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oin2T8Xp-1591953209821)(C:\Users\Rr\AppData\Roaming\Typora\typora-user-images\image-20200612170650041.png)]

与STL标准库的接口

  1. string允许使用C语言风格的字符串来进行初始化操作,反之不行

    string str = "123456"; // "123456"是C语言风格的字符串
    char str2[] = "123456";
    string str3 = str2;
    
    map<string, int> mp;
    mp[str2] = 123456;
    
  2. vector允许使用数组来进行初始化操作,反之不行

    int arr[] = {1,2,3,4,5,6};
    vector<int> v1(begin(arr), end(arr));
    vector<int> v2(begin(arr) + 1, end(arr) - 2);
    

多维数组

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

int a[3][4];
int b[3][4][5];

a是一个长为3的数组,其中数组的每个元素又是一个长为4的数组,该数组中每个元素为整数类型。
b是一个长为3的数组,其中数组的每个元素又是一个长为4的数组,该数组中每个元素为长为5的数组,该数组中元素为整形类型。

范围for语句处理数组

一维数组

int arr[] = {0, 1, 2, 3, 4};
for(auto &it: arr){
	it += 5;
}

多维数组(二维为例)

int arr[3][4] = {0 ... 11};
for(auto &row: arr){
	// 加上引用符号,row就是array[4]的一个别名
	for(auto col: row){
		cout << col << endl;
	}
}

for(auto row : arr){
	// 不加&时row是指针类型, 为 int* 类型指针
	for(auto col = row; col != row + 4; ++col){
		cout << *col;
	}
}

for(auto row = arr; row != end(arr); ++row){
	// row = arr 此时row为int (*row)[4]类型指针
	for(auto col = *row; col != end(*row); ++col){
		cout << *col << ' ';
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值