第四章——复合类型

数组

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

创建数组时应指出以下三点:

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

在C++中,可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明。如

int months[12];

上面一条语句的意思是,创建一个名为months的数组,该数组有12个元素,每个元素都可以存储一个int类型的值

声明数组的通用格式是:

typeName arrayName[arraySize];

 arraySize指定元素数目,它必须是整型常数(10、23)或const值,也可以是常量表达式,即其值在编译时都是已知的,不能是变量

数组可以单独访问数组元素

 方法是使用下标或索引来对元素进行编号。C++数组从0开始编号,用带索引的方括号来表示指定元素,例如months[0]表示months数组的第一个元素,months[11]表示数组的最后一个元素

数组的初始化

只有在定义数组的时候才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组

int hards[4] = {3,4,5,6};    //ok
int hands[4];                //ok
hands[4] = {5,6,7,8};        //error
hands = hards;               //error

 初始化数组时,提供的值可以少于数组的元素数目,如果只对数组的一部分进行初始化,则编译器把其他元素设置为0。

这里需要注意的是,C++11允许使用大括号的初始化方法(列表初始化)来进行数组的初始化。但是列表初始化禁止缩窄转换

 long lifts[]={24,56,3.0};

上面这条语句不能通过编译,因为将浮点数转换为整型是缩窄操作。

字符串

字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。第一种来自C语言,被称为C-风格字符串,另外一种是基于string类库的方法

存储在连续字节中的一系列字符意味着可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。

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

	char dog[8] = { 'b','e','a','u','x',' ','I','I' };//not a string
	char cat[8] = { 'f','a','t','e','s','s','a','\0' };// a string

 这两个数组都是char数组,但只有第二个数组是字符串

上述初始化方法需要使用大量单引号,还需要加上空字符,很繁杂。有一种将字符数组初始化为字符串的方法——只使用一个引号括起来的字符串即可,这种字符串被称为字符串常量或字符串字面值

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

用引号括起来的字符串隐式地包括结尾的空字符。

 应确保数组足够大,能够存储字符串中所有字符——包括空字符

拼接字符串常量

C++允许拼接字符串字面值,即将两个用引号括起来的字符串合并为一个。(任何两个由空白、制表符、换行符)分隔的字符串常量都将自动拼接成一个

strlen()只计算可见的字符。sizeof运算符指出整个数组的长度;

在数组中使用字符串

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

字符串输入

cin使用空白(空格、制表符和换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时只读入一个单词。

        每次读取一行字符串输入

        每次读取一个单词通常不是最好的选择,例如要求程序完整保存输入的New York,而不是只保存了New,需要采取另一种字符串读取方式

  • 面向行的输入:getline() 

getline()函数读取整行,它通过使用回车键输入的换行符来确定结尾。要调用这种方法,可以使用

cin.getline(name,number);

 该函数有两个参数,第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数

(如果这个参数为20,则它最多能读取19个字符,剩下的一个空间用于自动存储结尾的空字符)

getline()成员函数在读取指定数目的字符或遇到换行符时停止读取

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

  •  面向行的输入:get()

 istream类有另一个名为get()的成员函数,它的一种使用方式与getline()类似,接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get不再丢弃换行符,而是将其留在输入队列中。

cin.get(name,number);

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

cin.get()

 string类

 可以使用string类型的变量而不是字符数组来存储字符串

#include<iostream>
#include<string>
using namespace std;
int main()
{
	char ch[10] = "jaguar";
	string str = "pather";
	cout << "ch: " << ch << endl;
	cout << "str: " << str << endl;

}

 string对象和字符数组之间的主要区别是,可以将string对象声明为简单变量,而不是数组,而且类设计让程序能够自动处理string的大小,这使得与使用数组相比,使用string更方便

string对象的一些操作

  • 前面讲过不能将一个数组赋值给另一个数组,但可以将一个string对象赋给另一个string对象
  • string还可以使用+运算符将两个string对象合并起来,还可以使用+=运算符将字符串附加到string对象的末尾
  • 对C风格字符串,头文件cstring提供函数strcpy()将字符串复制到字符数组中,使用strcat()将字符串附加到字符数组末尾;
strcpy(charr1,charr2);    //copy charr2 to charr1
strcat(charr1,charr2);    //append contents of charr2 to charr1
  •  string类I/O。可以使用cin和运算符>>来将输入存储到string对象中,使用cout和运算符<<来显示string对象,其句法与处理C风格字符串相同;但是在每次读取一行时两者使用的句法不同;

对于C风格字符串使用方法是

cin.getline(name,number);

这种句点表示法表明,函数getline()是istream类的一个类方法(第一个参数是目标数组,第二个参数是数组长度) 

而对于string对象。使用方法是

getline(cin,name);

 这里没有使用句点表示法,表明这个getline()不是类方法,它将cin作为参数,指出到哪里去查找输入。

结构简介 

数组虽然可以存储多个元素,但是要求所有元素的类型必须相同。C++中的结构是一种比数组更灵活的数据格式,同一个结构中可以存储多种类型的数据。结构是用户定义的类型,而结构声明定义了这种类型的数据属性。

 

 关键字struct表明,这些代码定义的是一个结构的布局,标识符inflatable是这种数据格式的名称。这样便可以像创建char或int类型的变量那样创建inflatable类型的变量了。接下来的大括号中包含的是结构存储的数据类型的列表,其中每一个列表项都是一条声明语句。总之结构定义指出了新类型的特征。定义结构后,便可以使用这种类型的变量了。

#include<iostream>
#include<string>
using namespace std;

struct inflatable
{
	char name[20];
	float volume;
	double price;
};
int main()
{
	inflatable guest = { "Glorious",1.88,15.8 };
	inflatable pal = { "Audacious",3.12,23.9 };
	cout << "guest name: " << guest.name << " guest volume " << guest.volume;
}

 结构声名的位置很重要,一种可以放在main()函数中,紧跟在开始括号的后面,另一种选择是将声明放在main()的前面 ,位于函数外面的称为外部声明。外部声明可以被其后面的任何函数使用,而内部声明只能被该声明所属的函数使用。

共用体

共用体是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。也就是说,结构体可以同时存储int、long、和double,共用体只能存储int、long/或double。共用体的句法与结构体相似,但含义不同,例如

union one4all
{
    int int_val;
    double double_val;
    long long_val;
};

 可以使用one4all变量来存储int、long/或double,条件是在不同的时间进行:

one4all pail;
pail.int_val=15;
cout<<pail.int_val;
pail.double_val=1.78;
cout<<pail.double_val;

pail有时可以是int变量,有时可以是double变量。

成员名称标识了变量的容量,由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员,所以共用体的长度为其最大成员的长度。共用体的用途之一是当数据项使用两种或更多中格式(不会同时使用)时,可节省空间

枚举

enum的句法与使用结构相似,例如

enum spectrum{red,orange,yellow,green,blue,violet};

这条语句完成两项工作

  • 让spectrum成为新类型的名称;spectrum被称为枚举
  • 将red、orange等作为符号常量,它们对应整数值0~5。这些常量叫做枚举量

默认情况下,将整数值赋给枚举量,第一个枚举量的值为0,第二个枚举量的值为1,依次类推。

对于枚举,只定义了赋值运算符,没有为其定义算术运算

也可以使用赋值运算符来显式地设置枚举量的值

enum bits{one=1,two=2,four=4,eight=8};

 指定的值必须是整数,当然也可只显式地定义其中一些枚举量的值

指针和自由存储空间

前面谈到过计算机程序在存储数据时必须跟踪的3种基本属性(信息存储在何处、存储的值为多少、存储的信息是什么类型)

前面通过定义一个简单变量。声明语句指出了值的类型和符号名,还让程序为值分配内存,并在内部跟踪该内存单元。

下面用另外一种方法——指针。指针是一个变量,其存储的是值的地址,而不是值本身。对变量应用地址运算符(&)就可以获得它的位置。

声明和初始化指针 

指针声明必须指定指向的数据的类型

int* ptr;

这表明,*ptr的类型为int。由于*运算符被用于指针,因此ptr变量本身必须是指针。可以这样说ptr指向int类型、ptr的类型是指向int的指针。ptr是指针(地址),*ptr是int而不是指针。

 

 计算机需要跟踪指针指向的值的类型,例如char的地址与double的地址看上去没什么两样,但char和double使用的字节数是不同的,它们存储值时使用的内部格式也不同。

指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值,在这种情况下只能通过指针来访问内存。

#include<iostream>
using namespace std;
int main()
{
	int nights = 1001;
	int* pt = new int;    //allocate space for an int
	*pt = 1001;           //store a value there

	cout << "nights value = " << nights << " ,location " << &nights << endl;
	cout << "int ";
	cout << "value = " << *pt << " ,location " << pt << endl;
	return 0;
}

 

 程序使用new为int类型的数据对象分配内存。这是在程序运行时进行的,指针pt指向这个数据对象,如果没有pt,将无法访问这个数据单元。有了这个指针,就可以像使用变量那样使用*pt了,将值赋给*pt,从而将这些值赋给新的数据对象。

当需要内存时,可以使用new来请求,另外在使用完内存后,使用delete运算符能够将内存还给内存池,归还的内存可供程序的其他部分使用。

int *ps = new int;    //ok
delete ps;            //ok
delete ps;            //not ok

不要尝试释放已经释放的内存块,这样做的结果是不确定的。 

【只能用delete来释放使用new分配的内存,对空指针使用delete是安全的】

在使用new和delete时,应遵守以下规则

  • 不要使用delete释放不是new分配的内存
  • 不要使用delete释放同一个内存块两次
  • 如果使用new[ ]为数组分配内存,则应使用delete[ ]来释放
  • 如果使用new为一个实体分配内存,则应使用delete(没有方括号)来释放
  • 对空指针应用delete是安全的

指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。将整型变量加一后其值将增加一,但将指针变量加一后,增加的量等于它指向的类型的字节数。(将指向double的指针加1后,如果系统对double使用8个字节存储,则数值将增加8);另外C++将数组名解释为地址

猜你喜欢

转载自blog.csdn.net/yangSHU21/article/details/131607623