字符串是存储在内存的连续字节中的一系列字符,C++处理字符串的方式有两种。第一种来自C语言,常被称为C-风格的字符串(C-style string)。
存储在连续字节中的一系列字符意味着可以将字符串存储咋ichar数组中,其中每个字符都位于自己的数组元素中。字符串提供了一种存储文本信息的便捷方式,如提供给用户的消息(“请告诉我您的瑞士银行账号”)或来自用户的响应(“您肯定是在开玩笑”)。C-风格字符串具有一种特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾,例如,请看下面两个声明:
char dog[8] = {'b', 'e', 'a', 'u', 'x', 'I', 'I'}; //not a string!
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; //a string!
这连个数组都是char数组,但只有第二个数组是字符串。空字符对C-风格字符串而言是至关重要。例如,C++有很多处理字符串的函数,其中包括cout使用的那些函数。它们都逐个地处理字符串中的字符,直到到达空字符串为止。如果使用cout显示上面的cat这样的字符串,则将显示前7个字符,发现空字符后停止。但是,如果使用cout显示上面的dog数组(它不是字符串),cout将打印出数组中的8个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。由于空字符(实际上是被设置为0的字节)在内存中很常见,因此这一过程将很快停止。但尽管如此,还是不应将不是字符串的字符数组当作字符串来处理。
在cat数组示例中,将数组初始化为字符串的工作看上去冗长乏味——使用大量单引号,且必须记住加上空字符。不必担心,有一种更好的、将字符数组初始化字符串的方法——只需使用一个用引号括起的字符串即可,这种字符串被称为字符串常量或字符串字面值,如下所示:
char bird[11] = "Mr.Cheeps"; //the \0 is understood
char fish[] = "Bubbles"; //let the compiler count
用引号括起的字符串隐式地包括结尾的
字符串是存储在内存的连续字节中的一系列字符,C++处理字符串的方式有两种。第一种来自C语言,常被称为C-风格的字符串(C-style string)。
存储在连续字节中的一系列字符意味着可以将字符串存储咋ichar数组中,其中每个字符都位于自己的数组元素中。字符串提供了一种存储文本信息的便捷方式,如提供给用户的消息(“请告诉我您的瑞士银行账号”)或来自用户的响应(“您肯定是在开玩笑”)。C-风格字符串具有一种特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾,例如,请看下面两个声明:
char dog[8] = {'b', 'e', 'a', 'u', 'x', 'I', 'I'}; //not a string!
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; //a string!
这连个数组都是char数组,但只有第二个数组是字符串。空字符对C-风格字符串而言是至关重要。例如,C++有很多处理字符串的函数,其中包括cout使用的那些函数。它们都逐个地处理字符串中的字符,直到到达空字符串为止。如果使用cout显示上面的cat这样的字符串,则将显示前7个字符,发现空字符后停止。但是,如果使用cout显示上面的dog数组(它不是字符串),cout将打印出数组中的8个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。由于空字符(实际上是被设置为0的字节)在内存中很常见,因此这一过程将很快停止。但尽管如此,还是不应将不是字符串的字符数组当作字符串来处理。
在cat数组示例中,将数组初始化为字符串的工作看上去冗长乏味——使用大量单引号,且必须记住加上空字符。不必担心,有一种更好的、将字符数组初始化字符串的方法——只需使用一个用引号括起的字符串即可,这种字符串被称为字符串常量或字符串字面值,如下所示:
char bird[11] = "Mr.Cheeps"; //the \0 is understood
char fish[] = "Bubbles"; //let the compiler count
用引号括起的字符串隐式地包括结尾的
每次读取一行字符串输入
每次读取一个单词通常不是最好的选择。例如,加成程序要求用户输入城市名,用户输入New York或Sao Paulo。您希望程序读取并存储完成的城市名,而不是New 或Sao。要将整条短语而不是一个单词作为字符串输入,需要采用另一种字符串读取方法。具体的说,需要采用面向行而不是面向单词的方法。幸运的是,istream中的类(如 cin)提供了一些面向行的类成员函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入序列中。下面将详细介绍它们,首先介绍getline()。
1、面向行的输入:getline()
getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。要调用这种方法,可以使用cin.getline()。该函数有两个参数。第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多就读取19个字符,余下的空间将用于存储自动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。
例如,假设要使用getline()将姓名读入到一个包含20个元素的name数组中。可以使用这样的函数调用:cin.getline(name,20);
这将把一行读入到name数组中——如果这行包含的自读不超过19个(getline()成员函数还可以接受第三个可选参数)
2、面向行的输入:get()。。
我们来试试另一种方法。istream类有另一个名为get()的成员函数,该函数有几种变体。其中一种变体的工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get并不在读取并丢弃换行符,二十将其留在输入队列中。假设我们连续两次调用get():
cin.get(name, ArSize);
cin.get(dessert, ArSize); // a problem
由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符便是换行符。因此get()认为已到达行尾,而没有发现任何可读取的内容。如果不借助于帮助,get()将不能跨过该换行符。
幸运的是,get()已经有另一种变体。使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),伊尼茨可以用它来处理换行符,为读取下一行输入做好准备。也就是说,可以采用下面的调用序列:
cin.get(name, ArSize); // read first line
cin.get(); // read newline
cin.get(dessert, ArSize); // read second line
另一种使用get()的方式是将两个类成员函数拼接起来(合并),如下所示:
cin.get(name, ArSize).get(); // concatenate member funtions
之所以这样做,是由于cin.get(name, ArSize)返回一个cin对象,该对象随后将被用来调用get()函数。同样,下面的语句将把输入的连续的两行分别读入数组name1和name2中,其效果与两次调用cin.getline()相同:
cin.getline(name1, ArSize).getline(name2, ArSize);
其他形式的字符串字面值
除char类型外,C++还有类型wchar_t;而C++11新增了类型char16_t和char32_t。可创建这些类型的数组和这些类型的字符串面值。对于这些类型的字符串面值,C++分别使用前缀L,u和U表示,下面是一个如何使用这些前缀的例子:
wchar_t title[] = L"Chief Astrogator"; //w_char dtring
char16_t name[] = u"Felonia Ripova"; // char_16 string
char32_t car[] = U"Humber Super Snipe"; // char_32 string
C++新增的另一种类型是原始(raw)字符串。在原始字符串中,字符表示的就是自己,例如,序列\n不表示换行符,
永远跟党走,心中有党,事业理想。
⣿⣿⣿⣿⣿⠟⠋⠄⠄⠄⠄⠄⠄⠄⢁⠈⢻⢿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⠃⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⡀⠭⢿⣿⣿⣿⣿
⣿⣿⣿⣿⡟⠄⢀⣾⣿⣿⣿⣷⣶⣿⣷⣶⣶⡆⠄⠄⠄⣿⣿⣿⣿
⣿⣿⣿⣿⡇⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠄⠄⢸⣿⣿⣿⣿
⣿⣿⣿⣿⣇⣼⣿⣿⠿⠶⠙⣿⡟⠡⣴⣿⣽⣿⣧⠄⢸⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣾⣿⣿⣟⣭⣾⣿⣷⣶⣶⣴⣶⣿⣿⢄⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⡟⣩⣿⣿⣿⡏⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣹⡋⠘⠷⣦⣀⣠⡶⠁⠈⠁⠄⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣍⠃⣴⣶⡔⠒⠄⣠⢀⠄⠄⠄⡨⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣦⡘⠿⣷⣿⠿⠟⠃⠄⠄⣠⡇⠈⠻⣿⣿⣿⣿
⣿⣿⣿⣿⡿⠟⠋⢁⣷⣠⠄⠄⠄⠄⣀⣠⣾⡟⠄⠄⠄⠄⠉⠙⠻
⡿⠟⠋⠁⠄⠄⠄⢸⣿⣿⡯⢓⣴⣾⣿⣿⡟⠄⠄⠄⠄⠄⠄⠄⠄
⠄⠄⠄⠄⠄⠄⠄⣿⡟⣷⠄⠹⣿⣿⣿⡿⠁⠄⠄⠄⠄⠄⠄⠄⠄
永远跟党走,心中有党,事业理想。
⣿⣿⣿⣿⣿⠟⠋⠄⠄⠄⠄⠄⠄⠄⢁⠈⢻⢿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⠃⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⡀⠭⢿⣿⣿⣿⣿
⣿⣿⣿⣿⡟⠄⢀⣾⣿⣿⣿⣷⣶⣿⣷⣶⣶⡆⠄⠄⠄⣿⣿⣿⣿
⣿⣿⣿⣿⡇⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠄⠄⢸⣿⣿⣿⣿
⣿⣿⣿⣿⣇⣼⣿⣿⠿⠶⠙⣿⡟⠡⣴⣿⣽⣿⣧⠄⢸⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣾⣿⣿⣟⣭⣾⣿⣷⣶⣶⣴⣶⣿⣿⢄⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⡟⣩⣿⣿⣿⡏⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣹⡋⠘⠷⣦⣀⣠⡶⠁⠈⠁⠄⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣍⠃⣴⣶⡔⠒⠄⣠⢀⠄⠄⠄⡨⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣦⡘⠿⣷⣿⠿⠟⠃⠄⠄⣠⡇⠈⠻⣿⣿⣿⣿
⣿⣿⣿⣿⡿⠟⠋⢁⣷⣠⠄⠄⠄⠄⣀⣠⣾⡟⠄⠄⠄⠄⠉⠙⠻
⡿⠟⠋⠁⠄⠄⠄⢸⣿⣿⡯⢓⣴⣾⣿⣿⡟⠄⠄⠄⠄⠄⠄⠄⠄
⠄⠄⠄⠄⠄⠄⠄⣿⡟⣷⠄⠹⣿⣿⣿⡿⠁⠄⠄⠄⠄⠄⠄⠄⠄
china NO.1
结构简介
假设要存储有关篮球运动员的信息,则可能需要存储他(她)的姓名、工资、身高、体重、平均得分、命中率、助攻次数等。希望有一种数据格式可以将所有这些信息存储在一个单元中。数组不能完成这项任务,因为虽然数组可以存储多个元素,但所有元素的类型必须相同。也就是说,一个数组可以存储20个int,另一个数组可以存储10个float,但用一个数组不能再一些元素中存储int,在另一些元素中存储float。
C++总的结构可以满足要求(存储篮球运动员的信息)。结构是一种比数组更灵活的数据格式,因为同一个结构可以存储多种类型的数据,这书的能够将有关篮球运动员的信息放在一个结构中,从而将数据的表示合并到一起。如果要跟踪整个球队,则可以使用结构数组。而结构也是C++OOP堡垒(类)的基石。学习有关结构的知识将使我们离C++的核心OOP更近。
结构是用户定义的类型,而结构声明定义了这种类型的数据属性。定义了类型后,便可以创建这种类型的变量。因此创建结构包括两步。首先,定义结构描述——它描述并标记了能够存储在结构中的各种数据类型。然后按描述创建结构标量(结构数据对象)。
例如,假设Bloataire公司要创建一种类型来描述其生产线上冲去产品的成员。具体的说,这种类型应存储产品名称、容量(单位为立方英尺)和售价。下面的结构描述能够满足这些要求:
struct inflatable //structure declaration
{
char name[20];
float volume;
double price;
};
关键字struct表明,这些代码定义的是一个结构的布局。标识符inflatable是这种数据格式的名称,因此新类型的名称为inflatable。这样,便可像创建char或int变量那样创建inflatable类型的变量了。接下来的大括号中包含的是结构存储的数据类型的列表,其中每个列表项都是一条声明语句。这个例子使用了一个适合用于存储字符串的char数组、一个float和一个double。列表中的每一项都被称为结构成员,因inflatable结构中有三个成员。总之,结构定义指出了新类型的特征。