1.第4章 复合类型
4.2字符串
4.2.2在数组中使用字符串
- strlen()函数返回的是存储在数组中的字符串长度,而不是数组本身的长度。另外,strlen()只计算可见的字符,而不把空字符计算在内。例如:int name1[15] = "Basicman";则strlen(name1) = 8。
4.2.3字符串输入
#include <iostream>
using namespace std;
int main(void)
{
const int Arsize = 20;
char name[Arsize];
char desert[Arsize];
cout << "Enter your name:\n";
cin >> name;
cout << "Enter your favorite desert:\n";
cin >> desert;
cout << "I have some delicious " << desert;
cout << " for you." << name << endl;
system("pause");
return 0;
}
cin使用空白(空格、制表符、换行符)来确定字符串的结束位置。
4.2.4每次读取一行字符串输入
getline()和get()都读取一行输入,直到达到换行符。随后getline()将丢弃换行符,而get将换行符保留在输入序列中。
1.面向行的输入getline()
语法:cin.getline(数组名称,字符数);
例如,假设要使用getline()将姓名读入到一个包含20个元素的name数组中,cin.getline(name,20);字符数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。
#include <iostream>
using namespace std;
int main(void)
{
const int Arsize = 20;
char name[Arsize];
char desert[Arsize];
cout << "Enter your name:\n";
//cin >> name;
cin.getline(name, Arsize);
cout << "Enter your favorite desert:\n";
//cin >> desert;
cin.getline(desert, Arsize);
cout << "I have some delicious " << desert;
cout << " for you." << name << endl;
system("pause");
return 0;
}
getline()函数每次读取一行,它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,用空字符来替换换行符。
2.面向行的输入:get()
get()不丢弃换行符,将其留在输入队列中。假设调用两次get():
cin.get(name,Arsize);
cin.get(desert,Arsize); //a problem
第一次调用后,换行符留在输入队列中,第二次调用看到的第一个字符便是换行符。因此get()认为已达行尾,而没有发现任何可读取的内容。
解决方式:
1.cin.get(name,Arsize);
cin.get();
cin.get(desert,Arsize);
2.cin.get(name,Arsize).get();
cin.get(desert,Arsize).get();
3.空行及其他问题
get()读取空行后将设置失效位,接下来的输入将被阻断,但可以使用cin.clear();恢复输入。
4.混合输入字符串和数字
#include <iostream>
using namespace std;
int main(void)
{
cout << "What year was your house built?" << endl;
int year;
cin >> year;
cout << "what is its street address?" << endl;
char address[80];
cin.getline(address, 80);
cout << "Year built:" << year << endl;
cout << "address:" << address << endl;
cout << "Done" << endl;
system("pause");
return 0;
}
解决方法:在读取地址之前先读取并丢弃换行符。
1.cin >>year;
cin.get(); //or cin.get(ch);
2.(cin>>year).get(); //or (cin>>year).get(ch);
4.3string类简介
4.3.2赋值、拼接和附加
1.不能将一个数组赋给另一个数组,但可以将一个string对象 赋给另一个string对象。
例如:char charr1[20];char charr2[20] = "jaguar"; charr1 = charr2; //×
string str1; string str2 = "panther"; str1 = str2; //√
2.string类简化了字符串合并操作。可以用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾。
4.5共用体(P94、P95)
能够存储不同的数据类型,但只能同时存储一种类型。共用体常用来节省内存。
4.6枚举
1.设置枚举变量的值
- 可以使用赋值运算符显式地设置枚举变量的值。enum bits{one = 1,two = 2,four = 4,eight = 8};
- 指定的值必须是整数。也可以只显式地定义其中一些变量的值。enum big{fist,second =100,third};first默认为0,后面没有初始化的枚举变量的值比前面的枚举变量大1。因此third值为101。
- 可以创建多个值相同的枚举量。enum big{zero,null = 0,one,numero_nuo = 1};其中,zero和null都为0,one和numero_nuo都为1。
2.枚举的取值范围
上限:找到枚举量的最大值,找到大于最大值的最小的2的幂,将它减去1,得到上限。如,最大枚举值是101,则在2的幂中,比这个数大的最小值为128,因此取值范围上限为127。
下限:找到枚举量的最小值。如果它大于等于0,下限为0。如果是负数,与寻找上限方式相同,但最后加上负号。例如,最小枚举量为-6,而比它小的、最大的2的幂是-8,因此下限为-7。
4.7指针和自由存储空间
4.7.4使用new来分配内存
- typeName *pointer_name = new typeName;例如int *pn = new int;需要在两个地方指定数据类型:用来指定需要什么样的内存和用来声明合适的指针。
- 常规变量声明分配内存块:int higgens;int *pt = &higgens; new分配内存块:int *pn = new int;new从堆区中分配内存。
4.7.5使用delete释放内存
1.int *ps = new int; //allocate memory with new
......... //use the memory
delete ps; //free memory
2.释放ps指向的内存,但不会删除ps指针本身,例如,可以将ps重新指向另一个新分配的内存块。
内存泄漏:不配对使用new和delete,一直new而不释放,被分配的内存再也无法使用。
3.不能释放已经释放的内存。
4.不能使用delete释放声明变量所获得的内存,只能用来释放new分配的内存。
4.7.6使用new来创建动态数组
静态联编:在编译时给数组分配内存。
动态联编:在运行阶段创建需要的数组并选择长度。动态数组。
1.使用new来创建动态数组
int *psome = new int [10]; //new元素返回第一个元素的地址,该地址被赋值给指针psome。
delete [] psome; //方括号告诉元素释放整个数组,而不仅仅是指针指向的元素。
2.使用new和delete应遵守的规则
- 不要使用delete来释放不是new分配的内存。
- 不要使用delete释放同一个内存块两次。
- 如果使用new[]为数组分配内存,则应使用delete [] 来释放。
- 对控制指针应用delete 是安全的。
3.使用动态数组
访问动态数组:将指针当作数组名即可。int *psome = new int [10];则第一个元素psome[0],第二个元素psome[1]..........
4.8指针、数组和指针算术
1.将整数变量加1后,其值将增加1。但将指针变量加1后,增加的量等于它所指向类型的字节数。
2.arrayname[i] = *(arrayname + i) pointername[i] = *(pointername + i)
3.数组名和指针名区别:
(1)可以修改指针的值,而数组名是常量。
pointername = pointername+1; //vaild
arrayname = arrayname+1; //not allowed
(2)对数组应用sizeof()运算符得到的是数组的长度,而对指针应用sizeof()得到的是指针的长度。
4.8.4使用new来创建动态结构
1.创建结构: inflatable *ps = new inflatable; 将足以存储inflatable结构的一块可用内存的地址赋给ps。
2.访问结构成员:如果标识符是结构名,则使用句点运算符。如果标志符是指向结构的指针,则使用箭头运算符。还有一种方法是,如果ps是指向结构的指针,则*ps就是被指向的值——结构本身,因此(*ps).price是该结构的price成员。
3.一个使用new和delete的示例
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
char* getname(void)
{
char temp[80];
cout << "Enter the last name:";
cin >> temp;
char* pn = new char[strlen(temp) +1];
strcpy(pn, temp); //copy string into smaller space
return pn;
}
int main(void)
{
char* name;
name = getname();
cout << name << " at " << (int*)name << endl;
delete[] name;
name = getname();
cout << name << " at " << (int*)name << endl;
delete[] name;
system("pause");
return 0;
}
getname()函数返回一个指向输入字符串的指针。该函数将输入读入到一个大型的临时数组中,然后使用new创建一个刚好嫩刚好能存储该输入字符串的内存块,并返回一个指向该内存块的指针。(节省内存)
4.8.5自动存储、静态存储和动态存储
c++3种管理数据内存的方式:自动存储、静态存储、动态存储。
1.自动存储:局部变量 在所属函数被调用时自动产生,在该函数结束时消亡 通常存储在栈中 后进先出 在程序执行过程中栈不断地增大和缩小
2.静态存储:整个程序执行期间都存在 静态变量有两种:一种是在函数外面定义它,另一种是在声明变量时使用关键字stattic。
3.动态存储: 堆区
4.10数组的替代品
4.10.1模板类vector
模板类vector 类似于string类,也是一种动态数组。基本上,他是使用new创建动态数组的替代品。
- 必须包含头文件 #include
- 语法: vector vt; n_elem可以是整型常量,也可以是整型变量。例如,vector vd;
4.10.2模板类array
- 头文件 #include
- 语法:array arr; n_elem不能是变量,只能是常量。
4.10.3比较数组、vector对象和array对象
#include <iostream>
#include <vector>
#include <array>
using namespace std;
int main(void)
{
//数组
double a1[4] = { 1.2,2.4,3.6,4.8 };
//vector对象
vector<double> a2(4);
a2[0] = 1.0 / 3.0;
a2[1] = 1.0 / 5.0;
a2[2] = 1.0 / 7.0;
a2[3] = 1.0 / 9.0;
//array对象
array<double, 4> a3 = { 3.14,2.72,1.62,1.41 };
array<double, 4>a4;
a4 = a3;
cout << "a1[2]:" << a1[2] << " at " << &a1[2] << endl;
cout << "a2[2]:" << a1[2] << " at " << &a2[2] << endl;
cout << "a3[2]:" << a1[2] << " at " << &a3[2] << endl;
cout << "a4[2]:" << a1[2] << " at " << &a4[2] << endl;
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]:"