1、数组
声明格式:float loans[20];
loans的类型不是“数组”,而是“float 数组”。这强调数组是使用float类型创建的。其中,方括号中的元素个数必须为整型常数或const值,也可以是常量表达式(如8*sizeof(int)),但不能是变量,因为变量的值是在程序运行时设置的。
需要注意的是:C++数组从0开始编号,C++使用带索引的方括号表示法来指定数组元素。例如months[0]是months数组的第一个元素,months[11]是最后一个元素。注意,最后一个元素的索引比数组长度小1.
(1)有效下标值的重要性
编译器不会检查使用的下标是否有效。例如,如果将一个值赋给不存在的元素months[101],编译器并不会指出错误。但是程序运行后,这种赋值可能引发问题,它可能破坏数据或代码,也可能导致程序异常终止。所以必须保证程序使用只使用有效的下标。
(2)初始化数组
int yamcosts[3] = {20, 30, 5};
只需提供一个用逗号分隔的值列表(初始化列表),并将它们用花括号括起来即可。如果没有初始化函数中定义的数组,则其元素值将是不确定的。这意味着元素的值为以前驻留在该内存单元中的值。
当数组中元素数目与初始化器中值的数目不想同时情况。只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组:
int cards[4] = {3, 6, 8, 10};
int hand[4] ;
hand[4] ={5, 6, 7, 9}; //这种是不正确的
hand = cards; //不正确
要想给数组赋值,必须使用下标分别给数组中的元素赋值。如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0。
2、字符串
字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。一种是来自C语言,常被称为C-风格字符串(C-style string)。另一种是基于string类库的方法。
(1)C-风格字符串
存储在连续字节中的一系列字符意味着将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。C-风格的字符串有一种特殊的性质:以空字符结尾,空字符被写为\0。其ASCII码为0,用来标识字符串的结尾。例如下面两个字符串:
这两个数组中,只有第二个是字符串。空字符对C-风格字符串而言至关重要。C++有很多处理字符串的函数,其中包括cout使用的那些函数。它们都逐个的处理字符串中的字符,直到到达空字符为止。
例如上面两个数组,如果用cout输出字符串,第二个数组输出到\0就结束了,所以会输出7个字符;但是第一个数组并没有\0字符,所以cout会一直输出后面内存中的字符,直到遇到\0为止。虽然也很容易会有\0存在,但是还是不应该将不是字符串的字符数组当作字符串来处理。
如果像上面那样用{}和‘’处理字符串,工作会变得很麻烦。不必担心,有一种更好的、将字符数组初始化为字符串的方法----只需使用一个用引号括起来的字符串即可,这种字符串被称为字符串常量或字符串字面值。如下所示:
char bird[11] = "Mr. Cheeps";
char fish[] = "Bubbles";
用引号括起来的字符串隐式的包括结尾的空字符,因此不用显式的包括它。
①关于字符串的长度:
注意区分两个运算符:sizeof运算符指出整个数组的长度,比如说char cat[12] = "hello";,用sizeof(cat)得到的是12,即数组的长度;但是使用strlen函数返回的是存储在数组中的字符串的长度,而不是数组本身的长度,而且不包括空字符在内,所以上例strlen(cat)得到的是5,即hello的长度。
②关于字符串的输入:
字符串的输入存在两个缺陷,第一个缺陷是cin使用(空格、制表符和换行符)来确定字符串的结束位置;第二个缺陷是输入的字符串可能比目标数组长。
先看第一个例子:
来看一下这个例子的输出结果:
看看有什么问题呢?cin在获取字符串数组输入时只读取一个单词,读取该单词之后cin把该字符串放到数组中,并自动在结尾添加空字符。
这个例子的结果是cin把Alistair作为第一个字符串,并把它放到name数组中,把Dreeb留在输入队列中。当cin在输入队列中搜索用户喜欢的甜点时,它发现了Dreeb,因此cin读取Dreeb,并将它放在dessert数组中。
第二个缺陷的例子是cin不能防止将包含30个字符的字符串放到20个字符的数组中。
后面章节会介绍到cin的高级功能会解决这两个问题,大家不要着急。
③每次读取一行字符串输入
每次读取一个字符串显然不是很好的选择,比如程序要求输入城市名New York,使用cin得到的只是第一个单词New,显然这并不是你想要的完整的城市名。要想将整条短语而不是单词作为字符输入,需要采用另一种字符串读取方法。具体地说,需要采用面向行而不是面向单词的方法。
istream中的类正好提供了这样的两个函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入队列中。下面将分别介绍这两个函数。
a、面向行的输入:getline()
getline()函数读取整行,使用回车键输入的换行符来确定输入结尾,但是并不将换行符保存到队列中。要调用这种方法,可以使用cin.getline()。
例如:假设使用getline()将姓名读入到一个包含20个元素的name数组中,可以使用这样调用:
cin.getline(name,20);
第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。
同样用上节中的例子:
该程序现在可以读取完整的姓名和用户喜欢的甜点了。getline()函数每次读取一行,它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,它用空字符来替换换行符。
b、面向行的输入:get()
istream类有另一个名为get()的成员函数,该函数有几种变体用法如下:
其中一种与getline()类似,参数相同,并且都读取到行尾,但是get()将换行符留在输入队列中,假如我们连续两次调用get():
cin.get(name,ArSize);
cin.get(dessert,Arsize);
由于第一次调用后,换行符留在输入队列中,因此第二次调用时看到的第一个字符就是换行符,get()认为已经到行尾了,而没有发现任何可读取的内容。所以在上面两条语句中,name中存储了一行字符串,而dessert中是空的。
get()还有另一种变体。即使用不带任何参数的cin.get()调用读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行做好准备。上面语句可以换成如下:
cin.get(name,ArSize);
cin.get();
cin.get(dessert,Arsize);
这样第二个语句就把换行符读取了,然后再读第三条语句就没有影响了。
还有一种方式就是使用get()的方式将两个类成员函数拼接起来(合并),如下所示:
cin,get(name, ArSize).get();
同样,getline()也可以这么用:
cin.getline(name1,ArSize).getline(name2,ArSize);
还是上面的例子,可以这么表达:
从上面几个例子的对比我们可以总结一下,输入一行完整的字符串的方法有这么几种:使用cin.getline(name,20)或者使用cin.get(name,20).get()。那么我们应该选择使用哪一种呢?
当然,都可以,但是使用get()输入更仔细。例如,假如使用get()将一行读入数组中,如何知道停止读取的原因是由于已经读取了整行,而不是由于数组已经填满了呢?查看下一个字符,如果是换行符,说明已读取了整行,否则,说明该行中还有其他输入。总之,getline()使用起来更简单一些,但get()使得检查错误更简单些。
④混合输入字符串和数字
混合输入数字和面向行的字符串会导致跟上面同样的问题。
用户根本没有输入地址的机会,问题在于当cin读取年份之后,将回车键留在了输入队列中,后面的getline()看到换行符认为是一个空行,并将这个空字符串给了address数组。解决的方法也就是上面介绍的几种:
cin>>year;
cin.get();
或者(cin>>year).get(); 或者 (cin>>year).get(ch);
C++程序常使用指针(而不是数组)来处理字符串,以后会讲到。
(2)string类简介
ISO/ANSI C++98标准通过添加string类扩展了C++库,因此现在可以用string类型的变量而不是字符串数组来存储字符串。string类使用起来比数组简单,同时提供了将字符串作为一种数据类型的表示方法。
要使用string类,必须在程序中包含头文件string。string类位于名称空间std中,因此需要使用using namespace std;指令。string类定义隐藏了字符串的数组性质,使得处理字符串像处理普通变量那样简单。
看下面的例子:
下面是运行结果:
从上面这个例子中可以看到,string对象和使用字符数组有很多相同之处:
可以使用C风格字符串来初始化string对象;
可以使用cin来将键盘输入存储到string对象;
可以使用cout来显示string对象;
可以使用数组表示法来访问存储在string对象中的字符(即可以将string对象看作一个数组,可以访问其中的某一个元素)。
字符串数组和string对象的主要区别是:可以将string对象声明为简单变量,而不是数组:
string str1; string str2 = "panther";
类设计让程序能够自动处理string的大小。例如,str1的声明创建一个长度为0的string对象,但是将输入读取到str1中时,将自动调整str1的长度;这使得与数组相比string对象更方便,也更安全。
①赋值、拼接和附加
string类的操作比使用数组时更简单。例如,不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象。例如: string str1; string str2 ="pantner"; str1 = str2;
string类简化了字符串的合并操作。可以使用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象末尾。如 string str3; str3 = str1+str2; str1+=str2;
处理string对象的语法通常比使用C字符串函数简单,尤其是执行较为复杂的操作时。例如:
str3 = str1 + str2;
使用C-风格字符串时,需要使用的函数如下:strcpy(charr3,charr1); strcat(charr3, charr2);
另外,使用字符数组时总是存在目标数组过小而无法存储指定信息的危险,如下例:char site[10] = "house";
strcat(site, " of pancakes"); 函数strcat()试图将全部12个字符复制到数组site中,这将覆盖相邻的内存。这可能道枝程序终止或程序数据被破坏。而string类具有自动调整大小的功能,从而避免这种问题的发生。
确定字符串字数的方法也有区别:int len = str1.size(); int len2 = strlen(charr1);
②string类的I/O
可以使用cin或cout来输入存储到string对象或输出,其句法与处理C-风格字符串相同。但是每次读取一行而不是一个单词。