第四章 复合类型
4.1. 数组
创建语法:
typeName arrayName[arraySize];
或
typeName arrayName[arraySize] = {e1, e2, … , e(arraySize)};
4.1.2. 数组的初始化规则
只有在定义数组的时候才能使用初始化,以后就不能使用了,也不能将一个数组赋值给另一个数组。只能用循环一个个地来赋值。
如果只对数组的一部分进行初始化,编译器将会把其他元素设置为0。所以如果想要将数组中所有元素设置为0,可以直接:
long totals[500] = {0};
如果初始化数组的时候,不指定元素个数,编译器就会根据后面赋值的个数来对数组元素个数进行计算。
short things[] = {1, 5, 3, 8};
things数组将包含4个元素。
如何获得数组的大小呢
int num_elements = sizeof(things) / sizeof(short);
4.1.3. C++11数组初始化方法
- 可以省略等于号
- 可以不再大括号内包含任何元素,这种会把数组元素都设置为0
- 不允许缩窄转换。如浮点不能转int。
4.2. 字符串
什么是字符串?字符串就是多个字符。
所以说,字符串就是用char数组存储的一系列char类型元素。
C语言风格的字符串有一个特殊性质:以空字符结尾,也就是“\0”
char dog[8] = {'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'};
char cat[8] = {'f', 'a', 't', 'e', 'S', 's', 'a', '\0'};
这两个都是char数组,但只有第二个是空字符串。
如果使用cout分别输出这两个数组的话,cat能正常输出。而dog会在数组输出完之后继续输出相邻内存当中的数据并将其解释为字符,直到遇见\0才会停止。
还有一各创建字符串数组的方法。
char bird[11] = “Mr. Cheeps”;(这里的数组大小是加上\0之后的)
char fish[] = “Bubbles”;
就是用双引号来创建,而且不需要加上大括号。后面会自动加上空字符。
字符串常量(双引号)不能和字符常量(单引号)互换。
char shirt_size = ‘S’;
这个是将83进行赋值。
char shirt_size = “S”;
这是将“S”字符串所在的内存地址赋值给shirt_size。
4.2.1. 拼接字符串常量
使用cout输出的时候,任何两个由空白(空格、制表符、换行符)分割的字符串常量都会被自动拼接成一个。
4.2.2. 在数组中使用字符串
如果使用数组存储字符串的话,strlen()可以返回数组中字符串的长度。sizeof()返回的是数组的总大小。
另外,strlen返回的是可见的字符,不包括空字符。因此,如果要存储一个字符串,且使用strlen来算数组的大小的话,需要将strlen返回值+1.
4.2.3. 字符串输入
cin使用空白(空格、制表符和换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入的时候只会读取一个单词(如果是一句话的话)。读入第一个单词并且将它放入数组中知道,就会自动在结尾添加空字符。
还有一个问题,就是输入的字符串可能会比目标数组长。
4.2.4. 每次读取一行字符串输入
cin.getline()函数可以读取整行的输入,它通过回车键输入的换行符来确定输入结尾。
getline(arrayName, stringLength);
如:cin.getline(name, 20);
cin.get()函数也是面向行的输入。但是区别就是,get不会丢弃换行符,而是把它保留在输入队列当中。
如果连续两次调用get():
cin.get(name, ArSize);
cin.get(dessert, ArSize);
第二次调用的时候,get看见的第一个字符就是换行符,因此无法读取内容
可以使用一个cin.get()来处理换行符,为读取下一行输入做好准备。
另一种方法是cin.get(name, ArSize).get();
cin.get(name, ArSize)返回的是cin对象,还可以用来调用get()函数。
因此,cin.getline也可以连续调用下去,与两次分别调用的效果是相同的。
当getline或者get读取空行的时候,下一条输入语句将在前一条getline或者get结束读取的位置开始读取。现在的话,get读取空行后将设置失效位,这意味着接下来的输入将会被阻断。可以使用cin.clear()来恢复输入。
如果输入的字符串比分配的空间长,那么getline和get会把剩下的字符留在输入队列当中,getline还会设置失效位并关闭后面的输入。需要调用clear来恢复输入的。
4.2.5. 混合输入字符串和数字
混合输入数字和面向行的字符串会导致问题。
也就是如果使用cin输入数字和字符串的时候会导致问题。
问题在于,cin读取年份(数字),将回车键生成的换行符留在的输入队列当中。getline看到的是换行符。所以下面的就没法输入了。
解决方法就是在输入完数字之后,丢弃换行符,之后再读取字符串。
调用没有参数的get,或者利用(cin >> year).get()
4.3. string类
使用string类必须要引入头文件
#include <string>
4.3.1. 字符串初始化
string str1 = "rowenci1";
string str2 = {"rowenci2"};
string str3 {"rowenci3"};
4.3.2. 赋值、拼接和附加
因为string是对象,所以可以直接进行赋值。
拼接直接使用+
4.3.3. string类的其他操作
strcpy(char1, char2); //copy char2 to char1
strcat(char1, char2); // append contents of char2 to char1
当然,如果使用字符数组的时候,也可以用strcat,但是如果数组过小,会报错
char site[10] = “house”;
strcat(site, " of pancakes");
然后是两种确定字符串中字符数的方法
- int len1 = str1.size();
- int len2 = strlen(char1);
4.3.4. string类I/O
cin cout getline get
4.3.5. 其他形式的字符串字面值
char16_t和char32_t
wchar_t title[] = L"Chief Astrogator";
char16_t name[] = u"Felonia Ripova";
char32_t car[] = U"Humber Super Snipe";
还有一种的表示原始的字符串,也就是\n不在表示换行符,而是正常字符。
原始字符串将 “( 和 )” 用作定界符,并使用前缀R来进行标识。
cout << R"(rowenci “\n”)" << ‘\n’;
第一个换行符显示,第二个不显示,且不显示括号
如果要在原始字符串中显示括号的话,就用R"+*(
和)+*"
4.4. 结构体简洁
结构体
struct inflatable{
char name[20];
float volume;
double price;
};
使用点.来访问结构体当中的各个成员。
4.4.2. c++11结构体初始化
inflatable duck {“Daphne”, 0.12, 9.98};
inflatable mayor {}; // 数字为0,字符为空
4.4.4. 其他结构体属性
可以同时完成定义结构体和创建结构变量的工作。
只需要将变量名放在结束括号的后面就行。
struct perks{
int key_number;
char car[12];
} mr_smith, ms_jones;
struct perks{
int key_number;
char car[12];
} mr_glitz = {
7,
"Packard"
}
还可以声明没有名称的结构类型
struct{
int x;
int y;
} position;
这种结构体无法创建新的变量,只能使用position。
4.4.5. 结构体数组
inflatable guests[2] = {
{"Bambi", 0.5, 21.99},
{"Godzilla", 2000, 565.99}
};
4.4.6. 结构体中的位字段
C++也允许指定占用特定位数的结构成员。
struct torgle_register{
unsigned int SN : 4;
unsigned int : 4;
bool goodIn : 1;
bool goodTorgle : 1;
}
torgle_register tr = {14, true, false};
4.5. 共用体
union是一种数据格式,能且只能同时存储一种数据类型。
也就是说,结构体能同时存int,long等。而共用体只能存这些当中的一种。
定义起来和结构体很相似。
用途:当数据项使用两种或者更多种格式的时候可以节省空间。比如在商品结构体当中,有的商品ID是整数,有的是字符串,这样可以在结构体里面嵌入共用体。来表示整数ID。
匿名共用体没有名称,其成员将成为位于相同地址处的变量。
struct widget{
char brand[20];
int type;
union{
long id_num;
char id_char[20];
};
};
// ```
widget prize;
// ```
if(prize.type == 1) cin >> prize.id_num;
else cin >> prize.id_char;
因为union是匿名的,所以id_num和id_char会被视为widget的成员。它们两个的地址相同所以不需要给union命名。
4.6. 枚举
enum是用来创建符号常量的方式。
enum spectrum{
red,
orange,
yellow,
green,
blue,
violet,
indigo,
ultraviolet
};
spectrum ban;
band = blue; // right
band = 2000; // fault
4.6.1. 设置枚举变量的值
cnum bits{
one = 1,
two = 2,
four = 4,
eight = 8
};
不赋值的话就是0,后面没有被初始化的会比前面的大1.
4.6.2. 枚举的取值范围
enum bits{
one = 1,
two = 2,
four = 4,
eight = 8
};
bits myflag;
myflag = bits(6)
4.7. 指针和自由存储空间
如果home是一个变量
那么&home就是它的地址
*运算符被称为间接值或解除引用运算符
int * p,*左右的空格随便弄,
int* p1, p2表示创建指针p1和变量p2,所以每个指针变量都要用一个*
4.7.4. 使用new来分配内存
int* pn = new int;
int higgens;
int* pt = &higgens;
4.7.5. 使用delete释放内存
int* ps = new int;
delete ps;
只能用delete来使用使用new分配的内存。然而,对空指针使用delete是安全的。
4.7.6. 使用new来创建动态数组
int* psome = new int[10];
delete [] psome;
方括号是用来告诉程序应该释放整个数组,而不仅仅是指针指向的元素。
如果使用new的时候不带方括号,那么delete时也不该带。
反之相同。
4.8. 指针、数组和指针算术
根据内存地址来进行计算
4.9. 类型组合
各种类型之间也可以组合,比如结构体和union或者数组。
4.10. 数组的替代品
vector
#include <vector>
using namespace std;
vector<int> vi;
int n;
cin >> n;
vector<double> vd(n);
vector vt(n_elem);
array
#include <array>
using namespace std;
array<int, 5> ai;
array<double, 4> ad = {1.2, 2.1, 3.43, 4.3};
array<typeName, n_elem> arr;