C++学习要点总结(第四章)(一)

第四章 复合类型

4.1 数组

数组(array)是一种数据格式,能够存储多个同类型的值。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素。

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

  • 存储在每个元素中值的类型
  • 数组名
  • 数组中的元素数

声明数组的通用格式如下:

typeName arrayName[arraySize];

注意:arraySize它必须是整型常数或const值,也可以是个常量表达式,即所有值在编译的时候都是已知的。

数组可以单独访问数组元素,方法是通过使用下标或索引来对元素进行编号。(C++数组从0开始编号。)

小tips:编译器不会检查使用的下标是否有效。但是程序运行后,下标无效可能会引发问题,它可能会破坏数据结构或代码,也可能会导致程序的终止。所以必须确保程序只使用有效的下标值。

下面这段代码说明了数组的一些属性,包括声明数组、给数组元素赋值以及初始化数组。

#include<iostream>
using namespace std;
int main()
{
	int yams[3];//创建一个含三个元素的数组
	yams[0] = 7;//依次对第一个第二个第三个元素进行赋值操作,不能对yams[3]赋值,为无效下标
	yams[1] = 8;
	yams[2] = 6;
	int yamcosts[3] = { 20,30,5 };//对yamcosts这个数组进行初始化及赋值操作
	cout << "Total yams=";
	cout << yams[0] + yams[1] + yams[2] << endl;
	cout << "The package with " << yams[1] << " yams costs";
	cout << yamcosts[1] << "cents per yam." << endl;
	int total = yams[0] * yamcosts[0] + yams[1] * yamcosts[1] + yams[2] * yamcosts[2];
	cout << "The total yam expense is " << total << " cents.\n";
	cout << endl;
	cout << "size of yams array=" << sizeof yams << "bytes" << endl;
	cout << "size of one element=" << sizeof yams[0] << "bytes" << endl;
	return 0;
}

注意:sizeof运算符用于数组名的时候,得到的将是整个数组中的字节数。但如果将sizeof用于数组元素,则得到的将是元素的长度(单位为字节)。这表明yams是一个数组,而yams[1]只是一个int 变量。

4.1.1 数组的初始化规则

只有在定义数组的时候才能使用初始化,此后就不能对数组进行初始化操作了。也不能将一个数组赋给另一个数组。然而,可以使用下标分别给数组中的元素赋值。初始化数组的时候,提供的值可以少于数组的元素数目。(int yams[5]={1,2,3};编译器会把未提供值的元素设为0)

如果初始化数组时方括号内([])为空,C++编译器将计算元素个数。(通常情况下让编译器计算元素个数是种很糟的做法。如果主要关心的问题是程序,而不是自己是否知道数组的大小,则可以这样做: short things[]={1,5,3,8}; int num_elements=sizeof things/sizeof(short);)

4.1.2 C++11数组初始化方法

C++11将使用大括号的初始化作为一种通用初始化方式,可用于所有类型。

C++11中的列表初始化新增了一些功能:

  • 初始化数组的时候可以省略等号。例如int yams[5]={1,2,3,4,5};int photos[3]{1,2,3};
  • 可以不在大括号中包含任何东西,这将把所有元素都设置为0。
  • 列表初始化禁止缩窄转换。(缩窄转换是指:将浮点数转换为整数,从取值范围大的浮点数转换为取值范围小的浮点数(在编译期可以计算没有超出范围的表达式可以),从整数转换为浮点数,从取值范围大的整数转换为取值范围小的整数(在编译器可以计算没有超出范围的表达式),还有不能超出char型(数组类型)的取值范围。)

4.2 字符串

字符串是存储在内存的连续字节中的一系列字符。

C-风格字符具有一种特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾。

char dog[3]={'a','b','\0'};//是字符串

char dog[3]={'a','b','c'};//不是字符串

在cout打印的时候,遇到空字符就停止。

char birds[11]="Mr.Cheeps";

char fish[]="Bubbles";

以上两个初始化方式都可以,用引号括起的字符串隐式地包括结尾的空字符。

注意:在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。

注意:字符串常量(使用双引号)不能与字符常量(使用单引号)互换。字符常量如‘A’。但“A”不是字符常量,它表示的是两个字符A和\0。

4.2.1 拼接字符串常量

有时候字符串很长,无法放到一行中。C++允许拼接字符串字面值,即将两个用引号括起来的字符串合并为一个。事实上,任何两个由空白(空格、制表符和换行符)分隔打的字符串常量都将自动拼接称一个。

注意:拼接时不会在被连接的字符串之间添加空格。

4.2.2 在数组中使用字符串

要将字符串存储到数组中,最常用的方法有两种——将数组初始化为字符串常量、将键盘或文件输入读入到数组中。

下面一段代码,使用了strlen()和sizeof可以看一下它们的区别。

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	const int size = 15;
	char name1[size];
	char name2[size] = "C++owboy";
	cout << "Hody:I'm" << name2;
	cout << "What's your name?" << endl;
	cin >> name1;
	cout << "well," << name1 << ",your name has";
	cout << strlen(name1) << "letters and is stored" << endl;
	cout << "in an array of" << sizeof(name1) << "bytes" << endl;
	cout << "Your initial is " << name1[0] << '.' << endl;
	name2[3] = '\0';
	cout << "here are the first 3 characters of my name:";
	cout << name2 << endl;
	return 0;
}

strlen()只计算可见的字符,而不把空字符计算在内。

4.2.3 字符串的输入

字符串的输入中存在一个缺陷。下面程序将揭示该缺陷。

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	const int size = 100;
	char name[size];
	char dessert[size];
	cout << "Enter your name:" << endl;
	cin >> name;
	cout << "Enter your favourite dessert:" << endl;
	cin >> dessert;
	cout << "I have some delicious " << dessert;
	cout << "for you, " << name << endl;
	return 0;
}

cin使用空白(空格、制表符和换行符来确定字符串的结束为止),这意味着cin在获取字符数组输入时只读取一个单词。读取该单词后,cin将该字符串放在数组中,并自动在结尾处添加空字符。

4.2.4 每次读取一行字符串输入

下面将介绍两个成员函数分别是getline()和get()。

1.面向行的输入:getline()

getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。如,cin.getline(name,20)将输入的值读取到一个包含20个元素,名字为name的数组中。

注意:getline()函数每次读取一行。它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串的时候,它用空字符来替换换行符。

下面一段代码将展示如何调用getline()。

#include<iostream>
using namespace std;
int main()
{
	const int size = 100;
	char name[size];
	char dessert[size];
	cout << "Enter your name:" << endl;
	cin.getline(name, size);
	cout << "Enter your favorite dessert " << endl;
	cin.getline(dessert, size);
	cout << "I have some delicious " << dessert;
	cout << "for you, " << name << endl;
	return 0;
}

-----------------------------2021.7.30 更新-------------------------------------------------------------------------------

2.面向行的输入:get()

该函数有几种变体,其中一种变体的工作方式与工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同。并且都读取到行尾。但get并不再读取并丢弃换行符,而是将其留在输入队列中。

下面举一段例子。

cin.get(name, ArSize);

cin.get(dessert, ArSize);// 在输入的过程中出现问题

由于第一次调用后,换行符将留在队列里,因此第二次调用时看到的第一个字符便是换行符。因此get认为此时已达到行尾,而没有发现任何可读取内容。如果不借助于帮助,get()将不能跨过该换行符。

但是get()有另一种变体。使用不带任何参数的cin.get()调用可读取下一个换行符(即使是换行符)。因此,可以使用它来处理换行符,为读取下一行输入做好准备。例如,

cin.get(name, ArSize);

cin.get();

cin.get(dessert, ArSize);

另一种使用get()的方式是将两个类成员函数拼接起来(合并),例如,cin.get(name, ArSize).get();可以这样做的原因是cin.get()返回一个cin对象,该对象随后被用来调用get()函数。

#include<iostream>
using namespace std;
int main() {
	const int size = 20;
	char name[size];
	char dessert[size];
	cout << "Enter your name:\n";
	cin.get(name, size).get();
	cout << "Enter your favoriate dessert:\n";
	cin.get(dessert, size).get();
	cout << "I have some delicious " << dessert;
	cout << " for you," << name << "." << endl;
	return 0;
}

注意:如果使用的是cin.get(),则编译器知道是要读取一个字符。如果使用的是cin.get(name, size),则编译器知道是要将一个字符串放入数组中。

总之,getline使用起简单点,但get()使得检查错误更简单些。

3.空行和其他问题

当getline()或get()读取空行时,会发生什么情况?最初的做法是吓一跳输入语句将在前一条getline()或get()结束读取的位置开始读取。但当前的做法是,当get()读取空行后将设置失效位。这意味着接下来的输入将被阻断。但可以使用cin.clear();来恢复输入。

另一个潜在的问题是,输入字符串可能比分配的空间长。如果输入行包含的字符数比指定的多,则getline()和get()将把余下的字符留在输入队列中,而getline()还会设置失效位,并关闭后面的输入。

4.2.5 混合输入字符串和数字

请看下面一段代码。

#include<iostream>
using namespace std;
int main()
{
	cout << "What year was your house built?\n";
	int years;
	cin >> years;
	cout << "What is its street address?\n";
	char adress[100];
	cin.getline(adress, 100);
	cout << "Year built:" << years << endl;
	cout << "Adress:" << adress << endl;
	return 0;
}

可以看到这一段的代码adress的值为空,这是因为当cin读取年份,将回车键生成的换行符留在了输入队列中。后面的cin.getline()看到换行符后将认为这是一个空行,并将空字符赋给adress数组。改进的方法是:cin>>years; cin.get(); 或 (cin>>years).get();

4.3 string类简介

下面一段代码将介绍string对象与字符数组之间的一些相同点和不同点。

#include<iostream>
#include<string>
using namespace std;
int main() {
	char charr1[20];
	char charr2[20]="candle";
	string str1;
	string str2 = "candle";
	cout << "Enter a kind of feline:";
	cin >> charr1;
	cout << "Enter another kind of feline:";
	cin >> str1;
	cout << "Here are some felines:\n";
	cout << charr1 << " " << charr2 << " " << str1 << " " << str2 << endl;
	cout<<"The third letter in"<< charr2 << " is " << charr2[2] << endl;
	cout << "The third letter in" << str2 << " is " << str2[2] << endl;
	return 0;
}

string对象和字符数组之间的主要区别是,可以将string对象声明为简单变量,而不是数组。类设计让程序能够自动处理string的大小(将根据输入自动调整string的长度)。这使得与使用数组相比,使用string对象更方便,也更安全。

4.3.1 C++11字符串初始化

char first_date[]={"Le Chapon Dodu};

char first_date[]{"a bc d e"};

string str1={"The big park"};

string str1{"the beautiful flower"};

4.3.2 赋值、拼接和附加

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

string类简化了字符串合并操作。可以使用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾。

下面一段代码展示了如何使用+和+=。

#inlcude<iostream>
using namespace std;
int main()
{
	string s1 = "penguin";
	string s2, s3;
	cout << "you can assign one string object to another: s2=s1\n";
	s2 = s1;
	cout << "you can assign a C- style string to a string object.\n";
	s2 = "buzzard";
	cout << "s2:" << s2 << endl;
	cout << "you can concatenate strings: s3=s1+s2\n";//concatenation n.一系列
	s3 = s2 + s1;
	cout << "s3:" << s3 << endl;
	cout << "you can append strings.\n";
	s1 += s2;
	cout << "s1+=s2 yields s1=" << s1 << endl;
	s2 += " for a day";
	cout << "s2+=\" for a day\" yields s2= " << s2 << endl;//此处\"表示双引号而不是字符串的结尾。
	return 0;

}

4.3.3 string类的其他操作

头文件cstring提供了函数strcpy()将字符串复制到字符数组中,使用函数stract()将字符串附加到字符组末尾。

下面一段代码对用于string对象的技术和用于字符数组技术进行了比较。

#include<cstring>
#include<iostream>
#include<string>
using namespace std;
int main()
{
	char charr1[20];
	char charr2[20] = "jaguar";
	string str1;
	string str2 = "panther";
	str1 = str2;
	strcpy_s(charr1, charr2); 
	str1 += " paste";
	strcat_s(charr1, " juice"); 
	int len1 = str1.size();
	int len2 = strlen(charr1);
	cout << "the string " << str1 << " contains " << len1 << " characters." << endl;
	cout << "the string " << charr1 << " contains " << len2 << " characters." << endl;
	return 0;

}

4.3.4 string类I/O

可以使用cin和运算符>>来将输入存储到string对象中,使用cout和运算符<<来显示string对象。但每次读取一行而不是一个单词时,使用的句法不同。

下面一段代码将说明如何读取一行。

#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main()
{
	char charr[20];
	string str;
	cout << "length of string in charr before input: " << strlen(charr) << endl;
	cout << "length of string in str before input: " << str.size() << endl;
	cin.getline(charr, 20);
	cout << "you entered: " << charr << endl;
	cout << "length of string in charr after input: " << strlen(charr) << endl;
	getline(cin, str);
	cout << "you entered: " << str << endl;
	cout << "length of string in str after input: " << str.size() << endl;
	return 0;
}

注意:在用户输入之前,该数组charr的字符串长度是不确定的,因为第一个空字符的出现位置是随机的,因此在运行的时候,很可能得到不同的初始长度数值。

4.3.5 其他形式的字符串字面值

下面是关于如何使用前缀的例子。

wchar_t title[]=L"fleg charlet";

char16_t name[]=u"Felonia Ripova";

char32_t car[]=U"humber super";

C++11新增的另一种类型是原始字符串。原始字符串将"(和)"用作定界符,并使用前缀R来标识原始字符串。例如,cout<<R"(Jim "KING" Tutt uses "\n" instead of endl.)"<<'\n';

上述代码将显示为:Jim "KING" Tutt uses \n instead of endl.

如果原始字符串中包含)",则可使用R"+*(... )+*"来标识。可将前缀R与其他字符串前缀结合使用,以标识wchar_t等类型的原始字符串,如UR,Ru,LR等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值