第4章 复合类型
- 数组
- C风格字符串
- string类字符串
- getline(), get()
- 混合输入字符串和数字
- 结构
- 共用体
- 枚举
- 指针
- new, delete管理动态内存
- 动态数组
- 动态结构
- 自动存储、静态存储、动态存储
- vertor, array
4.1 数组
数组声明应指出三点:
- 存储在每个元素中的值的类型
- 数组名
- 元素数。必须是确定的,空着的话有初始化时的元素个数来确定
typeName arrayName[arraySize];
4.2 字符串
C风格字符串,以空字符结尾,被写作\0,其ASCII码为0。空字符对C风格字符串很重要,很多处理字符串的函数,遇到空字符才停止。
字符串常量(字面值)初始化字符串,数组的大小要把空字符考虑进去。
char dog[3] = {'d', 'o', 'g'}; // 不是字符串,没以空字符结尾
char cat[4] = {'c', 'a', 't', '\0'}; // 是字符串
char bird[11] = "Mr. Cheeps"; // 是字符串,自动加上空字符
char fish[] = "Bubbles"; // 是
4.2.1 拼接字符串常量
char kg[] = “kaige is “ “sb”;
4.2.2 在数组中使用字符串
sizeof运算符指出整个数组的长度,而strlen()只计算可见的字符。
4.2.3 字符串输入
cin的缺陷是,使用空白(空格、制表符、换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时只读取一个单词。
4.2.4 每次读取一行字符串输入
- getline():丢弃换行符
- get():将换行符保留在输入序列中
cin.getline(name, 20);
cin.get(name, ArSize); // read first line
cin.get(); // read newline
cin.get(dessert, Arsize); // read second line
带参数的cin.get是将一个字符串放入数组中,不带参数可读取下一个字符。这样跳过换行符。
4.2.5 混合输入字符串和数字
会出现问题
4.3 string类简介
string类定义隐藏了字符串的数组性质,能像处理普通变量那样处理字符串。需要包含头文件string,string类位于名称空间std中。
自动调整大小,可以将char数组视为一组用于存储一个字符串的char存储单元,而string类变量是一个表示字符串的实体。
4.3.1 C++字符串初始化
可以用列表初始化C风格字符串和string对象。
4.3.2 赋值、拼接和附加
4.3.3 string类的其他操作
这样处理C风格字符串
- strcpy():将字符串复制到字符数组中
- strcat():将字符串附加到字符数组末尾
- size():str.size(),string的长度
- strlen(charr1):char数组的长度
4.3.4 string类I/O
4.3.5 其他形式的字符串字面值
4.4 结构简介
4.4.1 在程序中使用结构
通常使用放在main函数外的外部声明。C++不提倡使用外部变量,但提倡使用外部结构声明。
4.4.2 C++结构初始化
4.4.3 结构可以将string类作为成员吗
可
4.4.4 其他结构属性
与C结构不同,C++结构除了成员变量之外,还可以有成员函数。但是成员函数一般用在类中。
4.4.5 结构数组
可以创建元素为结构的数组。
4.4.6 结构中的位字段
位字段通常用在低级编程中。指定占用特定位数的结构成员。字段的类型为整型或枚举,接下来是冒号,冒号后面是一个数字,她指定了位数。可以使用没有名称的字段来提供间距。
struct torgle_register
{
unsigned int SN : 4; // 4 bits for SN value
unsigned int : 4; // 4 bits unused
bool goodIn : 1; // valid input (1 bit)
bool goodTorgle : 1; // successful torgling
}
torgle_reggister tr = {14, true, false};
...
if (tr.goodIn) // if statement covered in Chapter6
...
4.5 共用体
共用体(union),和结构相似,也能存储不同的数据类型,但只能同时存储其中一种。
union one4all
{
int int_val;
long long_val;
double double_val;
}
one4all pail;
pail.int_val = 15; // 存了一个int
pail.double_val = 1.38; // 存了一个double,丢掉了之前的int
以上pail有时是int,有时是double。共用体的长度为其最大成员的长度,为了有足够的空间。她的用途之一,是数据项使用两种或更多格式(但不会同时使用)时,可节省空间。(对于内存很多的系统,没必要;对于嵌入式等编程,内存很宝贵,就能用到?)
4.6 枚举
enum spectrum {red, orange, yellow, green, blue, violet, indigo, ultraviolet};
以上让spectrum成为新类型的名称,她是枚举(enumeration)。大括号内的red、orange、yellow作为符号常量,他们对应整数值0~7,这些常量叫做枚举量(enumerator)。
枚举量是整型,可被提升为int,但int类型不能自动转换为枚举类型。枚举没有定义算数运算符。
4.6.1 设置枚举量的值
enum bits{one=1, two=200, for}; // 默认0开始,可以显式定义,后面比前面大1
enum {zero, null=0, one, numero_nuo=1}; // 可以创建多个值相同的枚举量
4.6.2 枚举的取值范围
通过强制类型转换,可以将取值范围中的任何整数值赋给枚举变量,即使这个值不是枚举值。
enum bits{one=1, two=2, for=4, eight=8};
bits myflag;
myflag = bits(6); // 可
枚举的取值范围:最大值是最大值的最小的2的幂减1。(例如最大值是101,则枚举范围的最大值为127。)最小值是0或者类似最大值方法。
4.7 指针和自由存储空间
计算机程序在存储数据时必须追踪的3种基本属性:信息存储在何处,存储的值是多少,存储的信息是什么类型。
指针是一个变量,其存储的是值的地址,而不是值本身。对于一个常规变量,使用地址运算符&,可以获得她的位置。
OOP强调运行阶段(而不是编译阶段),更加灵活。
指针名表示的是地址,*运算符被称为间接值(indirect value)或解除引用(dereferencing)运算符,将其应用于指针,可以得到该地址处存储的值。
#include <iostream>
int main()
{
using namespace std;
int updates = 6;
int* p_updates;
p_updates = &updates;
// express values two ways
cout << "Values: updates = " << updates;
cout << ", *p_updates = " << *p_updates << endl;
// express address two ways
cout << "Addresses: &updates = " << &updates;
cout << ", p_updates = " << p_updates << endl;
// use pointer to change value
*p_updates = *p_updates + 1;
cout << "Now updates = " << updates << endl;
return 0;
}
以上p_updates指向updates,他们完全等价。
4.7.1 声明和初始化指针
指针声明必须指定指向的数据类型。
4.7.2 指针的危险
一定要在对指针应用解引用*之前,将指针初始化为一个确定的、适当的地址。C++种创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向数据的内存。
下面是不对的:
long* fellow; // create a pointer-to-long
*fellow = 223323; // place a value in never-never land
4.7.3 指针和数字
指针不是整型,但计算机通常把地址当作整数来处理。不能简单的将整数赋给指针,如果要将数字作为地址来只用,应通过强制类型转换将数字转换为适当的地址类型。
int* pt;
pt = 0xB8000000; // type mismatch
pt = (int*) 0xB8000000; // types now match
4.7.4 使用new来分配内存
对于小型程序,声明一个简单变量方便;对于大型数据(如数组、字符串、结构),应使用new。如果通过声明来创建数组,编译时(静态联编static binding)将为他分配内存空间,不管程序最终是否使用这个数组,她都在哪里,占用了内存。
使用new时,如果在运行阶段需要用到,则创建它,否则不创建。(动态联编dynamic binding)
静态联编时,必须在编写程序时指定数组的长度;动态联编时,程序将在运行时确定数组的长度。动态数组(dynamic array)
int* psome = new int[10];
delete [] psome;
使用new和delete时,应遵守以下规则:
- 不要使用delete来释放不是new分配的内存。
- 不要使用delete释放同一个内存块两次。
- 如果使用new []为数组分配内存,则应使用delete []来释放。
- 如果使用new为一个实体分配内存,则应使用delete(没有方括号)来释放。
- 对空指针应用delete是安全的。
使用new创建动态数组后,她指向数组中的第一个元素,*psome是第一个元素的值。可以用下标来访问元素,例如psome[1]。psome = psome + 1,将指向第二个元素。
4.8 指针、数组和指针算数
指针和数组基本等价的原因在于指针算数(pointer arithmetic)和C++内部处理数组的方式。
将整数变量加1后,其值增加1;但将指针变量加1后,增加的量等于她指向的类型的字节数。
4.8.1 程序说明
4.8.2 指针小结
- 声明指针:typeName* pointerName;
- 给指针赋值:应将内存地址赋给指针。pn = &bubble; pc = new double[30];
- 对指针解除引用:对指针解除引用意味着获取指针指向的值,不要对未被初始化为适当地址的指针解除引用。cout << *pn; pn[0];
- 区分指针和指针所指向的值:如果pt是指向int的指针,*pt完全等同于一个int类型的变量。
- 数组名:多数情况下,C++将数组名视为数组的第1个元素的地址。但将sizeof用于数组名时,返回整个数组的长度。
- 指针算数:允许将指针和整数相加。
- 数组的动态联编和静态联编:使用数组声明来创建数组时,采用静态联编;使用new[]来创建数组,采用动态联编。
- 数组表示法和指针表示法:使用方括号数组表示法等同于对指针解除引用。
4.8.3 指针和字符串
数组和指针的特殊关系可以扩展到C风格字符串。
4.8.4 使用new创建动态结构
inflatable* ps = new inflatable;
#include <iostream>
struct inflatable // structure definitio
{
char name[20];
float volume;
double price;
};
int main()
{
using namespace std;
inflatable* ps = new inflatable; // allot memory for structure
cout << "Enter name of inflatable item: ";
cin.get(ps->name, 20); // method 1 for member access
cout << "Enter volume in cubic feet: ";
cin >> (*ps).volume; // method 2 for member access;
cout << "Enter price: $";
cin >> ps->price;
cout << "Name: " << (*ps).name << endl; // method 2
cout << "Valume: " << ps->volume << " cubic feet\n"; // method 1
cout << "Price: $" << ps->price << endl; // method 1
delete ps; // free memory used by structure
return 0;
}
4.8.5 自动存储、静态存储和动态存储
- 自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable)。在所属的函数被调用时自动产生,在该函数结束时消亡。局部变量,通常存储在栈中,后进先出。
- 静态存储:整个程序执行期间都存在的存储方式。
- 在函数外面定义的
- 声明变量时使用了关键字static
- 动态存储:new delete,被称为自由存储空间(free store)或堆(heap)。该内存池同以上两种是分开的,数据的生命周期不完全受程序或函数的生存时间控制。
要避免内存泄露,养成同时使用new和delete的习惯。
4.9 类型组合
4.10 数组的替代品
4.10.1 模板类vector
是使用new创建动态数组的替代品。实际上vector类确实使用new和delete来管理内存,但是自动完成的。
#include <vector>
...
using namespace std;
vector<int> vi; // 创建一个zero-size的int数组
int n;
cin >> n;
vector<double> vd(n) // 创建n个double类型元素的数组
4.10.2 模板类array(C++11)
长度固定,使用栈,和数组一样方便安全效率。
#include <array>
...
using namespace std;
array<int, 5> ai; // 创建一个数组对象,有5个int
array<double, 4> ad = {1.2, 2.1, 3.42, 4.3};
4.10.3 比较数组、vector对象和array对象
#include <iostream>
#include <vector> // STL C++98
#include <array> // C++11
int main()
{
using namespace std;
// C, original C++
double a1[4] = { 1.1, 2.2, 3.3, 4.4 };
// C++98 STL
vector<double> a2(4); // create vector with 4 elements
// no simple way to initialize in C98
a2[0] = 1.0 / 3.0;
a2[1] = 1.0 / 5.0;
a2[2] = 1.0 / 7.0;
a2[3] = 1.0 / 9.0;
// C++11 -- create and initialize array object;
array<double, 4> a3 = { 3.14, 2.72, 1.62, 1.41 };
array<double, 4> a4;
a4 = a3; // valid for array objects of same size
// use array notation
cout << "a1[2]: " << a1[2] << " at " << &a1[2] << endl;
cout << "a2[2]: " << a2[2] << " at " << &a2[2] << endl;
cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;
// misdeed,不端行为
a1[-2] = 20.2;
cout << "a1[-2]: " << a1[-2] << " at " << &a1[-2] << endl;
cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;
return 0;
}
- 数组、vector对象、array对象,都可以使用下标。
- 从地址可知,array和数组在相同的内存区域(栈);vector在另一个区域(自由存储区或堆)。
- 可以将array对象赋值给另一个array对象,数组必须主元素复制。
- a1[-2]在数组外面了,但能编译过去。用成员函数at()来做。
4.11 总结
数组、结构、指针。
4.12 复习题
4.13 编程练习
1
2
3 使用strcpy()和strcat()出现了C4996,在项目属性-C/C++–SDL检查,选择否,不检查此错误。
4
5 printf()输出string类的字符串时,需要调用c_str(),否则显示乱码。
6 略
7 略
8 cin下面连着用getline(),会产生name不能输入,直接结束的现象。通过调用cin.get()清空缓冲区来解决。(P68)
9 略
10 使用array需要包含头文件array,否则报使用不完整的类型。
999 Ref
- 错误C4996:https://blog.csdn.net/weixin_44171004/article/details/86675605
- c++中printf显示string类乱码的解决:https://blog.csdn.net/xx970829/article/details/115963735
- 连着使用cin和getline()只能输入一次的问题:https://blog.csdn.net/qq_41575507/article/details/104432933
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 cdd@ahucd.cn