第四章 复合类型
(1)数组(array)
概念:一种数据格式,能够存储多个同类型的值
每个值都存储在一个独立的数组元素中
用声明语句创建数组,声明语句包含“:
●存储在美国元素中的值得类型
●数组名
●数组中的元素数
typeName arrayName [arraySize]
e.g short months [12] ;
【arraysize指定元素数目,它必须是整型常数或const值或常量表达式(8*sizeof(int)),即它不能为变量】
【数组之所以被称为符合类型,是因为它是使用其他类型来创建的】
存在意义:可以使用个下标或索引来对元素进行编号从而可以单独访问数组元素
C++使用带索引的方括号表示法来指定元素 e.g months[0]是months数组的第一个元素‘
【C++数组从0开始编号】
【如果下标无效,把值赋给一个不存在的元素,编译器不会出错,但是这种赋值会破坏数据代码,也可能导致程序终止】
??怎么破坏??
★数组的初始化
1)C++允许在声明语句中初始化数组元素 e.g int yamcost [3] = {20, 30, 5};
2)初始化列表要用花括号括起,用逗号分隔元素
3)只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组
4)可以使用下标分别给数组中的元素赋值
5)初始化数组时,提供的值可以少于数组的元素数目,如果只对数组的一部分进行初始化,则编译器将吧其他元素设置为0
【将数组中所有元素初始化为0: 只要显示地将第一个元素初始化为0 或 不在大括号内包含任何东西】
6)如果初始化数组时方括号内([ ] )为空,C++编译器将计算元素个数
7)初始化数组时,可省略等号(=)
8)列表初始化禁止缩窄转换
(2)字符串(string)
定义:字符串是存储在内存的连续字节中的一系列字符
字符串常量或字符串字面值:使用引号括起的字符串
【用引号括起的字符串隐式地包括结尾的空字符】
【字符串常量(使用双引号)不能与字符常量(使用单引号)互换】
1)数组
字符串的定义以为这可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中——这就是C-风格字符串??
C-风格字符串的特殊性质:一定要以空字符(null character)结尾,空字符写作\0
【在创建这种数组时,要确保数组足够大,能够存储字符串中所有字符,包括空字符】
a)字符串初始化
C-风格字符串:char cat [8] = { 'f', 'a', 't', 'e', 's', 's', 'a', '\0' };
简化:char bird [11] = "Mr.Cheeps";
` b)字符串输入
最基础(但有缺陷):cin>>arrayName
【缺陷:cin使用空白(空格、制表符和换行符)来确定字符串的结束位置——即cin只读取一个单词】
每次读取一行字符串的输入:istream中的类(cin)提供了一些面向行的类成员函数getline()和get()
【每次都读取一行输入,知道到达换行符】
getline():使用cin.getline()调用该函数
有两个参数,第一个是存储输入行的数组名称,第二个是要读取的字符数
getline()成员函数在读取指定书目的字符或遇到换行符时停止读取
get():与前者最大不同是,在读取到行尾时并不会丢弃换行符,而是将其留在输入队列中
【由于第一次调用后,换行符将留在输入队列中,因此第二次调用看到的第一个字符便是换行符,因此get()认定已达到行尾】
参数与前者相似
调用它的变体cin.get()(不带任何参数)可读取下一个字符,因此一般用这个变体来处理换行符
还可以将两个类成员函数拼接起来(e.g cin.get(name, ArSize).get();)作用于两次调用cin.getline()相同
其他操作:
附加、复制:
【头文件cstring提供了相关函数】
strcpy(): 将字符串复制到字符数组中
e.g strcpy(charr1, charr2) ; // copy charr2 to charr1
strcat():将字符串附加到字符数组的末尾
e.g strcat(charr1, charr2);
strcat(charr1, "juice");
确认字符串中字符数(需要头文件cstring):
函数strlen()
用法:int len2 = strlen(charr1);
2)string类
【在使用string类,必须在程序中包含头文件string】
使用string对象的方式与使用字符数组在很多方面相同:
●可以使用C-风格字符串来初始化string对象
●可以使用cin来讲键盘输入存储到string对象中
●可以使用cout来显示string对象
●可以使用数组表示法来访问存储在string对象中的字符
主要不同:
●可以将string对象声明为简单变量
●类设计让程序能自动处理string的大小
●char数组可视为一组用于存储一个字符串的char存储,而string类变量是一个表示字符串的实体
string的初始化:
e.g string str2 = "panther";
e.g string third_date = {"The bread Bowl"};
e.g string fourth_date {"Hank 's Fine Eats" };
string的输入(输出):
cin&cout
getline(cin, str); 这不是istream类的一个类方法,它将cin作为参数,指出到哪里查找输入
string类的其他操作:
赋值、拼接和附加:
可以将一个string对象赋给另一个string对象,通过使用"="
简化了字符串合并操作,使用运算符"+"将两个string对象合并起来
可以使用运算符"+="将字符串附加到string对象的末尾
类似strcpy()和strcat():strncpy()和strncat()
确定字符串中的字数:函数size()
e.g int len1 = str1.size();
3)其他形式的字符串字面值:
……
原始(raw)字符串:
在原始字符串中,字符表示的就是自己
输入原始字符串是,按回车键不仅会移到下一行,还将在原始字符串中添加回车字符
原始字符串将"(和)"用作定界符,并使用前缀R来标识
如果要在原始字符串中包含)“,该语法允许在表示字符串开头的”和(之间添加任何字符,结尾也是相同的格式
自定义定界符是,可在默认定界符之间添加任何基本字符,但空格、左右括号、斜杠和控制字符(制表符和换行符)除外
(3)结构(structure)
定义:同一个结构可以存储多种类型的数据
结构是用户定义的类型,而结构声明定义了这种类型的数据属性
创建结构:
1、 定义结构描述
2、按描述创建结构变量
e.g: struct inflatable
{
char name[20];
float volume;
double price;
};
这些代码定义的是一个结构的布局,标识符inflatable是这种数据格式的名称 ——此新类型的名称为inflatable
大括号中包含的都是结构存储的数据类型的列表,其中每个列表项都是一条声明语句,每一项都被称为结构成员
inflatable hat;
C++允许在声明结构变量是省略关键字struct
访问各个成员:
hat 的类型是inflatable
成员运算符(.)可以用来访问各个成员
用法:hat . volume——结构的volume成员
hat . price——price成员
结构声明的位置:
main()函数中(内部声明):只能被该声明所属的函数使用
main()的前面(外部声明):可以被其后面的任意函数使用
【通常 使用外部结构声明】
初始化:
和数组一样,用花括号括起,由逗号分隔列表
列表初始化,等号可选
如果大括号未包含任何东西,各个成员都将被设为0
其他:
结构可以将string类作为成员
可以使用赋值运算符(=)将结构赋给另一个同类型的结构——成员赋值
结构数组:
定义:元素为结构的数组
e.g inflatable gifts [100];
【gifts是数组,使用gifts.price这样的表述是无效的】
cin>>gifts[0]. volume;
初始化结构数组:
结合初始化数组(逗号分隔每个元素的值,并将这些值用花括号括起)和初始化结构(逗号分隔每个成员的值,并将这些值用花括号括起)的规则
inflatable guests[2] =
{
{"Bambi", 0.5, 21.99},
{"Godzilla", 2000, 565.99}
};
(4)指针(pointer)
定义:指针是一个变量,其存储的是值得地址,而不是值得本身
相关运算符:
&:地址运算符,e.g home是变量,&home是它的地址
*:间接值(indirect value)或解除引用(dereferencing),将其用于指针,可以得到该地址处存储的值
声明和初始化指针:
指针声明必须指定指针指向的数据的类型
e.g int * p_updates;
* p_updates的类型为int
由于*运算符被用于指针,因此p_updates变量本身必须是指针
*p_updates是指针(地址),而*p_updates是int
*两边的空格是可选的
【一般我没说p_updates指向int类型(int *p_updates),或p_updates的类型是指向int的指针(int* p_updates)】
可以在声明语句中初始化指针,在这种情况下,被初始化的是指针,而不是它指向的值
e.g int higgens = 5;
int * pt = &higgens; 这里将pt(而不是*pt)的值设置为&higgens
【!!!一定要在 对指针使用解除引用运算符(*) 之前, 将指针初始化为一个缺点的、适当的地址】
【指针描述的是位置,整数是可以执行运算的数字,两者是不同的,因此不能简单地将整数赋给指针】
如果要将数字值作为地址来使用,应通过强制类型转换将数字转换为地址类型
e.g int * pt ;
pt = (int *) 0xB8000000 ;
new&delete运算符:
指针的真正用途是,在运行阶段分配为命名的内存以存储值,在这一阶段,只能通过指针来访问内存
new 的作用:在运行阶段,为一个已知类型的值分配内存,并返回其地址,在讲地址赋给指针
e.g int * pn = new int ;
new int告诉程序,需要适合存储int的内存,new根据类型确定内存大小并找到这样的内存返回其地址,地址被赋给pn。现在pn是地址,*pn是存储的值
格式: typeName * pointer_name = new typeName; (需要在两个地方指定数据类型:用来指定需要什么样的内存和用来声明合适的指针)
【通常慢变量的值都存储在被称为栈(stack)的内存区域中,而new从被称为堆(heap)或自由存储区(free store)的内存区域分配内存】
使用new来创建动态数组:
静态联编:在编译时给数组分配内存
动态联编:在运行阶段根据需要创建数组并选择数组长度
在运行阶段创建的数组叫动态数组
格式:type_name * pointer_name = new type_name [num_elements]; 【pointer_name指向第一个元素】
e.g int * psome = new int [10]; 只需将数组的元素类型和元素数目告诉new即可,必须加上方括号,括号内包含元素数目
delete [ ] psome; 方括号告诉程序,应释放整个数组
【如果使用new是不带方括号,则使用delete是,也不应带方括号,反之亦然】
delete 的作用:在使用完new 分配的内存后能够将其归还给内存池,归还或释放(free)的内存可供程序的其他部分使用
格式:delete后面要加上指向内存块的指针 e.g delete ps;
【一定要配对地使用new 和 delete,否则会发生内存泄漏(memory leak)——被分配的内存再也无法使用了】
【不能重复释放内存,不能使用delete来释放声明变量所获得的内存】
动态数组:在运行阶段创建的数组叫动态数组
new 语句提供了识别内存块中每个元素所需的全部信息
psome指向数组的第一个元素,所以*psome是第一个元素的值,
也可以将指针当做数组名使用:第一个元素是psome[0]
【psome = psome + 1; 的效果是,现在psome[0]指的是原来数组的第二个值,psome = psome - 1; 之后,还原】
指针、数组和指针算术:
指针和数组基本等价的原因在于指针算术(pointer arithmetic)和C++内部处理数组的方式
指针变量加1后,增加的量等于它指向的类型的字节数(如,指向double的指针加1,如果系统对double使用8个字节存储,则数值增加8)
???是地址的数值加8还是指向的量加8??我倾向于前者
C++将数组名解释为(数组第一个元素的)地址
使用数组表示法时,C++执行如下转换:
arrayname[ i ] becomes *(arrayname + i)
指针也是一样: pointername[ i ] becomes *(pointername + 1)
即*(stacks + 1)和stacks[1]是等价的
数组和指针的区别:
数组名是常量而指针的值可以修改
对数组应用sizeof运算符得到的是数组长度,而对指针得到的是指针的长度,计算指针指向的是一个数组
(5)共用体(union)
定义:能够存储不同的数据类型,但同一时间只能存储其中一种类型
共用体每次只能存储一个值,因此必须有足有空间来存储最大的成员
共用体的长度为其最大成员的长度
用途:当数据项使用两种或更多种格式(但不会同时使用)时,可节省空间
(6)枚举(enumeration)
定义???
e.g enum spectrum {red, orange, yellow, green, blue, violet,indigo,ultraviolet};
spectrum被称为枚举
red orange yellow等作为符号常量,分别对应0~7——这些常量叫做枚举量
默认情况下,将整数值赋给枚举量,第一个枚举量为0,第二个为1,以此类推
特殊属性:
不进行强制性转换时,只能将定义枚举时使用的枚举量赋给这种枚举的变量
对于枚举,值定义了赋值运算符
枚举量是整型,可被提升为int类型,但int类型不能自动转换为枚举类型
设置枚举量的值:
可以使用赋值符来显示地设置枚举量的值:
e.g emum bits {one = 1, two = 2, four = 4, eight = 8};
指定的值必须是整数,也可以只显示地定义其中一些枚举量的值
e.g enum bigstep{first, second = 100, third};
【这里first默认为0,后面没有被出书啊的枚举量的值将比其前面的枚举量大1】
可以创建多个值相同的枚举量