C++系列第六篇 数据类型下篇 - 复合类型(数组及字符串)

 系列文章

C++ 系列 前篇 为什么学习C++ 及学习计划-优快云博客

C++ 系列 第一篇 开发环境搭建(WSL 方向)-优快云博客

C++ 系列 第二篇 你真的了解C++吗?本篇带你走进C++的世界-优快云博客

C++ 系列 第三篇 C++程序的基本结构-优快云博客

C++ 系列 第四篇 C++ 数据类型上篇—基本类型-优快云博客

C++ 系列 第五篇 C++ 算术运算符及类型转换-优快云博客

前言

        之前已经进行了基本类型、计算运算符、类型转换方面的总结, 有需要可以翻看之前的文章。接下来主要进行复合数据类型的总结,包括数组、字符串、string类字符串、结构、共用体,枚举、指针,数据类型方面和C 没有大的出入,和前边文章一样,我们会通过大量的示例来说明对应的知识点。因为复合类型知识点比较多,所以我们会分成多个章节进行总结,本章主要总结数组和字符串相关知识。

数组

      概念

        数组(array)是一种数据格式,能够存储多个同类型的值。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素,数组元素在内存中是连续的,这一点很重要,很多性能优化和算法都会用到这个特性。

        要创建数组,可使用声明语句。数组声明应指出以下三点:

        1、存储在每个元素中的值的类型;

        2、数组名;

        3、数组中的元素数。

       声明

         typeName arrayName[arraySize];

         同C语言, C++数组的声明如上,可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明,如 int a -> int a[5]。

        例如,short months[12];就是声明了一个存储12个short类型的数组,数组名是months,可以将数组中的每个元素看作是一个简单变量。

        arraySize 指定元素数目,它必须是整型常数(如 10)或 const 值,也可以是常量表达式(如 8*sizeof(int)),即其中所有的值在编译时都是已知的。具体地说,arraySize 不能是变量,变量的值是在程序运行时设置的。

       数组为什么是复合类型

          数组之所以被称为复合类型,是因为它是使用其他类型来创建的(C 语言使用术语“派生类型”,但由于 C++对类关系使用术语“派生”,所以它必须创建一个新术语,即 复合)。不能仅仅将某种东西声明为数组,它必须是特定类型的数组。没有通用的数组类型,但存在很多特定的数组类型,如char 数组或long数组。

        还是上边的例子,short months[12]; months 的类型不是数组,而是short 数组,强调了months数组是使用short类型创建的。当然我们平常交流的时候也会说数组类型,但是要切记,数组一定是绑定类型的。

       数组最重要的特征

        可以单独访问数组元素。

        这一点很重要,我们上边说过这么一句话 "数组元素在内存中是连续的", 可以单独访问数组元素正是利用了这一点,通过数组元素类型 和 数组元素的下标值 即可推算出 数组元素在内存中的位置,进入进行直接访问。算法中使用数组一定是因为数组元素的随机访问性 及 cpu cache 局部访问特性 (后边会写操作系统相关的文章,再讲cpu cache 特性及数组是怎么高性能访问的)。

       数组初始化 

          如下列举里几种和 C 一样的初始化方式, 1)定义时初始化;2) 使用时赋值初始化;3)定义时部分初始化;4)自动计算数组个数初始化。 

          定义时初始化数组时,提供的值可以少于等于数组的元素个数,如果部分初始化,剩余未赋值的 元素默认为0 。

        如果初始化数组时方括号内为空,编译器将计算元素个数,但是让编译器去计算元素个数是个不推荐的做法,比如书写的时候少些一个值,数组就会少一个元素,以经验来说,什么时候会用到为空初始化的情况呢?是在初始化一个 大规格的常量字符串 数组时,我们可能会随着项目需求新增不同的常量字符串,如果使用方括号带值的初始化每次除了增加字符串,还需要修改 数组的个数,而使用方括号为空的数组,则增加字符串即可。

0b604e4b2d084c1daac1bbfc9ecd8972.png        和C 不同的初始化方法:

        C++11 增加了使用大括号的初始化(列表初始化)方式,可以用于所有类型,我们之前讲基本类型的时候 演示过。

        像 int array[3]={0,1,2}; 这种初始化方式本身就是列表初始化的方式,  单C++11 还有一些其它变种初始化方式 ,如下所示,可以省略 等于号 和 可以大括号内不包含任何东西,为空时,所有元素都设置为0.

038f08068148476bb73bfdf1190eabf3.png

字符串

        概念

        字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。第一种来自 C语言,常被称为C-风格字符串。另一种基于string 类库的方法。

       C-风格字符串

        存储在连续字节中的一系列字符意味着可以将字符串存储在 char 数组中,其中每个字符都位于自己的数组元素中。C-风格字符串具有一种特殊的性质:以空字符(null character)结尾,空字符被写作\0,其 ASCI 码为 0,用来标记字符串的结尾。

        如下这两个数组都是char 数组,但只有第二个数组是字符串。空字符对 C-风格字符串而言至关重要。C++有很多处理字符串的函数,其中包括 cout 使用的那些函数。它们都逐个地处理字符串中的字符,直到到达空字符为止。如果使用 cout 显示array2字符串,则将显示前2个字符,发现空字符后停止。但是,如果使用 cout 显示array1数组(它不是字符串),cout 将打印出数组中的4 个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。由于空字符(实际上是被设置为 0 的字节)在内存中很常见,因此也有可能只打印4个字符,但尽管如此,还是不应将不是字符串的字符数组当作字符串来处理。

15e14b0c415a499bbfbdf210a5b86123.png

         我们在实际使用中,很少会使用以上的方式去声明一个字符串的,太费劲,上边的举例只是为了说明字符串是一个字符数组。我们常用的方式如下,需要注意,用引号括起的字符串隐式的包括结尾的空字符,可以看到我们示例里得到的size 是包含了空字符的。示例中被双引号引起来的叫字符串常量。

c7b06d657ae741939a31c2eac3f81655.png

         一定要正确区分 字符 和 字符串  'A'  和 "A" 是不一样的,'A' 是一个字符,可以给一个char 变量赋值,而"A" 是一个字符串,有两个字符组成 (A 字符和 \0 字符)。

 字符串常量拼接

        有时候,字符串很长,无法放到一行中。C++允许拼接字符串常量,即将两个用引号括起来的字符串合并为一个。事实上,任何两个由空白(空格、制表符和换行符)分割的字符串常量都将自动拼接为一个。C语言也有对应的特性,如下C代码 和C++代码 输出效果是一样的

C代码

#include <stdlib.h>
#include <stdio.h>

#define test_string  "abcd" "efg"

int main()
{       
        printf("%s\n", test_string);
}


C++代码
#include <iostream>

int main() {
    using namespace std;

    cout << "abcd" "efg" << endl;

    return 0; 
}

字符串的输入

        如下示例,简单的使用cin 作为输入,在多单词的时候,输入被截断了,直到下一次要输入时,不等输入又 把之前截断并未输出的部分输出了。所以cin 其实是使用键盘输入的空字符来确认输入结束的,我们正常使用的时候,保证是需要有多单词输入的情况的,这就需要其他的按行输入的方法。

5d05d358d04e498d8545d895fdf6d94b.png

cin.getline 

        getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。该函数有两个参数。第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取 19 个字符,余下的空间用于存储自动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。

b58dc5fea3a84feba2b58b24dbd77481.png

cin.get 

        istream 类有另一个名为 get()的成员函数,该函数有几种变体。其中一种变体的工作方式与 getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但 get并不再读取并丢弃换行符,而是将其留在输入队列中。如下示例,因为保留了换行符,所以第二次要输入时,并不等输入之前输出了之前遗留的换行符。

7bde555218674857825fd3b5fa8f6eb8.png

        怎么解决呢?可以如下进行操作,之所以可以这样做,是由于cin.get(name,ArSize)返回一个 cin 对象,该对象随后将被用来调用 get()函数。读到这里会不会有疑惑为什么加一个.get() 就可以了呢,是因为C++允许函数有多个版本,条件是这些版本的参数列表不同。如果使用cin.get(name, size),则编译器知道是要将一个字符串放入字符数组中,因为使用适当的成员函数。如果使用的是cin.get(); ,则编译器知道是要读取一个字符,这就是C++里的函数重载。0022f7ed5d134bb6a9b4b80b4effc85e.png

        当然,我们在getline 里说过,getline 是使用通过回车键输入的换行符来确定输入结尾。所以如果在调用getline之前 输入队列中存在换行符,则getline 调用的时候也会发生之前读到之前的换行符就结束的问题,解决的办法就是提前读取并丢弃换行符,可以通过get()  或只接受一个 字符的 get(ch) 完成。

string类

        C++98 标准通过添加 string 类扩展了C++库,因此可以使用string类型的变量(使用 C++的话说是对象)而不是字符数组来存储字符串。string 类使用起来比数组简单,同时提供了将字符串作为一种数据类型的表示方法。要使用 string类,必须在程序中包含头文件string。string 类位于名称空间std 中,因此必须提供一条using 编译指令,或者使用 std::string 来引用它。string类定义隐藏了字符串的数组性质,使用户能够像处理普通变量那样处理字符串。

        从如下示例可知,在很多方面,使用string 对象的方式与使用字符数组相同, 可以使用C-风格字符串来初始化string对象,可以使用cin来将输入存储到string 对象中,可以使用cout来显示string对象,可以使用数组表示法来访问存储在string对象中的字符。

        类设计让程序能够自动处理 string 的大小。例如,str2 的声明创建一个长度为0 的 string 对象,但程序将输入读取到str2 中时,将自动调整str2 的长度。

37102dbabed14152b28f0ab761f0ecfe.png

        较C-风格更易用的点

        1、不能将一个数组赋给另一个数组,但可以将一个string 对象赋给另一个string对象。

        2、string 类简化了字符串合并操作。可以使用运算符+将两个 string 对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾,这种追加不像C 语言还需要考虑目前字符串的最大容量,string 对象会自动扩容。

        3、string 作为对象,自然就有方法,可以使用 str.size(); 技术 string 对象 str 的长度。

        string类的输入

        上边示例可知,可以使用cin 和运算符 >> 来将输入存储到 string 对象中,使用cout 和运算符<<来显示string 对象,其句法与处理C-风格字符串相同。但每次读取一行而不是一个单词时,使用的句法不同。

        string 类 需要 使用 getline(cin,str); 方式 将 输入写入  str 对象中,这里没有使用句点表示法,这表明这个 getline()不是类方法。它将 cin 作为参数,指出到哪里去查找输入。另外,也没有指出字符串长度的参数,因为 string 对象将根据字符串的长度自动调整自己的大小。

        那么,为何一个getline()是istream 的类方法,而另一个不是呢?在引入 string 类之前很久,C++就有istream 类。因此istream 的设计考虑到了诸如 double 和 int 等基本 C++数据类型,但没有考虑 string 类型,所以 istream 类中,有处理 double、int 和其他基本类型的类方法,但没有处理 string 对象的类方法。

5d17ef8538894d0ebf28a6b05d64b132.png

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值