写在前面:大家好!我是【AI 菌】,一枚爱弹吉他的程序员。我
热爱AI、热爱分享、热爱开源
! 这博客是我对学习的一点总结与记录。如果您也对深度学习、机器视觉、算法、Python、C++
感兴趣,可以关注我的动态,我们一起学习,一起进步~
我的博客地址为:【AI 菌】的博客
上一篇:【C++养成计划】玩转数字——数学运算函数/随机数(Day7)
昨天,我们学习了对有关数字的一常用些操作;今天来一起探索C++中两种最基本的数据结构:数组和字符串。
文章目录
- 1. 初探数组
- (1) 什么是数组
- (2) 数组的存储方式
- (3) 声明和初始化数组
- (4) 访问数组中的数据
- 2. 多维数组
- (1) 声明和初始化
- (2) 访问多维数组的元素
- 3. 动态数组
- 4. C风格字符串
- (1) C风格字符串中的风险
- (2) C风格的字符串函数
- 5. C++中的String类
1. 初探数组
(1) 什么是数组
在C++中,数组让你可以按照顺序将一系列相同类型的数据存储到内存中。
数组具有如下几个特点:
- 数组是一系列元素
- 数组中所有元素的类型都相同
- 这组元素组成一个完整的集合
(2) 数组的存储方式
数组的存储方式和图书馆中存放书的方式是类似的。想想书架上某一排上存放的书籍,这就是一个一维数组,因为它只沿着一个维度延伸。每本书都是一个数组元素,而书架就是为存储这些书籍而预留的内存。
同样的道理,整个书架就是一个二维数组,因为它不仅可以沿着横向延伸,还能沿着纵向延伸。那么一个图书室就可以看成是一个三维数组,因为一个图书室有很多个书架,每个书架还能按行和列摆放不同书籍,可以从3个不同维度延伸。如此说来,整个图书馆有很多个图书室,因此它可以沿着4个维度延伸,所以图书馆可看作是一个四维数组。依次类推,可以举例出n维数组。
存储数据的空间大小,也就类似于书占书架的位置多少。不同的是,数组中每一个元素所占用的空间大小是相同的,因为数组中所有元素的类型都相同。元素所占用的空间大小与元素的类型有关。比如一个int类型的元素占用4个字节的空间,一个double类型的元素占用8个字节的空间。
(3) 声明和初始化数组
数组声明
在 C++ 中要声明一个数组,需要指定元素的类型type、数组的名称arrayName和数组的长度length(也叫元素的个数),如下所示:
type arrayName[length];
例如,要声明一个元素类型为int,个数是5的数组ArrayNums
int ArrayNums[5];
数组初始化
在声明数组时,还能对数组初始化。将ArrayNums里的5个元素分别初始化一个值。
int ArrayNums[5]={8, 16, 32, 64, 128};
当然,也可以只对前面几个元素进行初始化,如下所示:
int ArrayNums[5]={8, 16}; //初始化前两个数
注:这里所声明的数组都是静态数组,即它们的长度都是固定的。
(4) 访问数组中的数据
访问数组中的元素,要使用从0开始的索引。比如存储在数组ArrayNums中的一个元素是ArrayNums[0],第二个元素是ArrayNums[1],依次类推。
访问数组时,不能超越其边界,否则结果是无法预料的。在很多情况下,这将导致程序崩溃。比如你可以在只包含5个元素的数组中取回索引为10的元素,但这样会给程序带来安全和稳定性方面的风险。
下面举一个简单的例子,声明并初始化一个int数组,并将其元素显示在屏幕上。
#include<iostream>
using namespace std;
int main()
{
int ArrayNums[5]={8, 16, 32, 64, 128};
for(int i=0;i<5;i++)
cout<<ArrayNums[i]<<endl; //逐一输出每一个元素
return 0;
}
运行结果:
除此之外,我们还可以修改数组中的数据。比如我们已经声明并初始化了一个数组ArrayNums,现在想修改数组中的第三个元素,可按如下方式进行赋值:
ArrayNums[2]=10; //将ArrayNums数组中第三个元素改为10
2. 多维数组
大于或等于2维的数组都称为多维数组,我们这里以最常见的二维数组进行讲解。
(1) 声明和初始化
多维数组声明
在C++中,要声明多维数组,可指定每维包含的元素数。比如,一个书架有2排,每排可以存放5本书,那么就可以按如下声明这个书架(二维数组):
int Bookshelf[2][5];
多维数组初始化
如果想给每本书指定(初始化)一个名称,假设这10本书的名称分别为0~9,则可按如下方式进行初始化:
int Bookshelf[2][5]={{0, 1, 2, 3, 4}, {5, 6, 7, 8, 9}};
同样,你也可以按如下方式进行初始化:
int Bookshelf[2][5]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
然而,第一种初始化方式更好,因为它能让你更容易看出数组的结构。
(2) 访问多维数组的元素
和一维数组一样,访问数组中的元素,要使用从0开始的索引。比如书架第1排上的第2本书是Bookshelf[0][1],第2排上的第5本书就是Bookshelf[1][4]。
下面演示一个简单的例子,在屏幕上显示出书架上每一本书的名称:
#include<iostream>
using namespace std;
int main()
{
int Bookshelf[2][5]={{0, 1, 2, 3, 4}, {5, 6, 7, 8, 9}};
for(int i=0;i<2;i++)
{
for(int j=0;j<5;j++)
cout<<Bookshelf[i][j]<<" "; //逐一输出每一个元素
cout<<endl;
}
return 0;
}
运行结果:
3. 动态数组
在前面我们声明的一维数组和二维数组都属于静态数组。静态数组的特点是,在数组声明时就确定了数组元素的个数,且在后期不能再改变。
但是在实际问题中,可能无法在一开始就能确定数组的长度。比如,要在应用程序中存储图书馆中的图书信息,你无法知道需要处理图书的上限数。就小图书馆来说,为稳妥起见,你可以对上限做合理的假设。但是在这种情况下,将预留大量的内存,从而降低了系统的性能。
为了减少占用的内存,可不使用前面介绍的静态数组,而使用动态数组,并在运行阶段根据需要调整数组大小。C++提供了std::vector类,这是一种方便且易于使用的动态数组。
想更深入了解vector类的用法和各种骚操作,可以戳戳这里:动态数组类:vector
注:对类等概念还不太了解的同学,此小节可先暂时不掌握;待后面我讲完类,可再来学习。
4. C风格字符串
(1) C风格字符串中的风险
C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持,是一种特殊的字符数组。字符串实际上是使用空字符 ‘\0’ 终止的一维字符数组。
下面我们采用C风格字符串,创建了一个 “Hello World” 。
char SayHello[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
请注意,该数组的最后一个字符为空字符‘\0’。这也被称为字符串结束字符,因为它告诉编译器字符串到此结束。这种C风格字符串是特殊的字符数组,因为总是在最后一个字符后加上空字符‘\0’。
当在初始化字符数组时忘记添加‘\0’,打印该数组时,Hello World后面会出现垃圾字符。如下所示:
#include<iostream>
using namespace std;
int main()
{
char SayHello[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
cout<<"加空字符输出:"<<SayHello<<endl;
char SayHello1[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
cout<<"不加空字符输出:"<<SayHello1<<endl;
char SayHello2[] = "Hello World";
cout<<"字符串形式输出:"<<SayHello2;
return 0;
}
运行结果如下:
分析:
第一行,末尾加了空字符’\0’的字符数组,输出情况正常。
第二行,末尾没加‘\0’的字符数组,输出情况异常,如图中红色框所示,出现了垃圾字符。
第三行,使用字符串常量的形式,可以省去末尾的’\0’,输出仍然正常。这是因为C++ 编译器会在初始化数组时,自动把 ‘\0’ 放在字符串的末尾。
对比可知,使用C风格字符串——一维字符数组,要特别注意空字符‘\0’的使用;否则在有些情况下,可能会引起程序崩溃,影响系统的稳定性。而使用C++中的字符串类string可以避免这些问题。
(2) C风格的字符串函数
C++中的cstring类中提供了很多实用的字符串函数,如下表所示:
在使用这些函数时,程序前面需要加上头文件:
#include <cstring>
下面举个简单的例子,使用上述的一些函数:
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char str1[] = {'H','e','l','l','o',' ','\0'};
char str2[] = {'C','p','p','\0'};
strcat(str1,str2);
cout<<"连接:"<<str1<<endl;
cout<<"连接后的长度:"<<strlen(str1)<<endl;
return 0;
}
运行结果:
注意:这些C风格字符串作为输入的函数非常危险,因为它们会寻找终止空字符‘\0’,如果程序员没有在字符数组末尾添加空字符,这些函数将跨越字符数组边界。
5. C++中的String类
为了避免以上的问题,C++提供了string类,这是一种功能强大而安全的字符串操作方式。无论是处理文本输入,还是执行拼接等字符串操作,使用这些C++标准字符串都是效率最高的方式。
详细了解String类的用法和各种骚操作,可以看我之前写的两篇博文,这里不再赘述。
第一篇:C++:string类的基本用法(一)
第二篇:C++:常用的string类字符串函数(二)