一、知识点回顾
1、数组(array)
(1)数组的基本信息
数组是一种数据格式,能够存储多个类型的值,要声明数组,可使用声明语句,但应该包含以下三点:
● 数组名
● 存储在每个元素中的值的类型
● 数组中的元素数
数组声明的通用格式如下:
TypeName arrayName [arraysize]
arraysize必须是整形常数或const值,也可以是常量表达式(具体地说不能是变量)。
(2)数组编号
数组的很多用途都是用于访问单独的数组元素,C++数组从0开始编号,然后利用索引来标识访问各个元素。
Ex:int moths[12]
moths[0]是moths数组的第一个元素.
(3)数组声明注意:
● 只有在定义数组是才能使用初始化,此后便不能使用并且,不能讲数组赋给另一个数组;
● 初始化数组时,提供的值可以少于数组的元素数目,为初始化的部分,编译器将把其元素设置为0.
(4)C++11中数组的初始化方法
● 初始化数组时可以省略等号.
● 在大括号内可以不包含任何东西,则所有元素设置为零.
● 列表初始化,禁止缩窄转换.
2、字符串(string)
(1)C-风格的字符串
● c-风格的字符串储存在char数组中,以’\0’(空字符)结尾;
● 字符常量使用单引号,字符串常量使用双引号,相互之间不可以互换;
● 拼接字符串常量,第一个字符串中的空字符将被第二个字符串取代
(2)在数组中使用字符串
● 可以使用初始化字符串常量或将键盘文件输入到数组中;
● sizeof返回整个数组的长度;
● strlen返回数组中可见字符串的长度,并且不计算空字符,所以,在确定数组长度的时候要+1;
(3)字符串输入
● 使用cin进行字符串的输入,cin使用空白(空格、tab、\n)来确定字符串的结束位置。
Ex:cin >> Mia WANG;
Cin把Mia当作第一个字符串,并且自动在结尾添加空字符,结束,抛却WANG。
● 输入字符串可能比目标数组长,只截取目标数组长度的字符串。
(4)每次读取一行字符串输入
A、面向行的输入:getline()
● getline()函数,了通过回车键输入的换行符来确定输入结尾;
● 使用cin.getline()调用该函数;
● 该函数有两个参数,第一个为数组名,第二个为要读取的字符串数;
Ex:cin.getline(arraynanme, arraysize);
● getline()成员函数在读取制定数目的字符或入到换行符时停止读取,随后用空字符替换换行符。
B、面向行的输入:get()
● get()函数与getline()函数工作方式类似,区别在于get()函数读取到行尾后,不再读取并丢弃换行符,而是将其留在输入队列中;
● 第一次丢用后,换行符将留在输入队列中,第二次调用时,看到的第一个字符便是换行符,get()函数认为已经到达队尾,不会再继续读取内容;
● 使用不带任何参数的cin.get()可读取的是下一个字符(即换行符),因此可以用它来处理换行符,为读取下一行输入做好准备。
Ex:cin.get(Name1, size);
cin.get();
cin.get(Name2, size);
● 另一种使用方式是将两个类成员函数拼接起来;
Ex:cin.get(Name1, size).get();
cin.getline(Name1, size).getline(Name2, size);
3、string类
(1)string类简介
可以使用string类型的变量而不是字符数组来存储字符串,且string类使用起来比数组简单,同时提供了将字符串作为一种数据类型的表示方法。
● 要使用string类,必须包含头文件名<string>;
● string类位于名称空间std中,使用时必须提供一条using编译指令,或者使用std :: string来引用;
● string类定义隐藏了字符串数组的性质,能够像处理普通变量一样处理字符串;
a、可以使用c-风格字符串来初始化string对象;
Ex:string str1;
string str2 = “panther”;
● 可以使用cin将键盘输入存储到string对象中;
Ex:cin >> str1;
● 可以使用cout对象来显示string对象;
Ex:cout << str1;
● 可以使用数组表示法来访问存储在string对象中的字符。
(2)C++字符串初始化、赋值、拼接和附加
● c++字符串初始化和数组相同;
● 可以将一个string类对象赋值给另一个对象;
Ex:string str1;
string str2 = “panther”;
str1 = str2;
● 可以使用“+”运算符将两个string类拼接;
● 可以使用“+=”运算符将字符串附加到string对象末尾。
(3)string类的其他操作
● strcpy(charr1, charr2) //copy charr2 to charr1;
● strcat(charr1, charr2) //oppend contents charr2 to charr1;
● strlen(charr)和str.size()
a、strlen()是个常规函数,接受一个C-风格字符串作为参数;
ex:strlen(charr1);
b、size()是string类的一个方法,使用”.”运算符来调用这个方法;
ex:str.size();
(4)string类I/O
● 未初始化的数组内容是未定义的;
● 未被初始化的string类对象长度自动设置为0;
● cin.getline(arrayname, size) ≠ getline(cin, str);
cin.getline()是一个istream的类方法,接受两个参数,目标数组及数组长度;
getline()是一个函数,接受两个参数,cin和字符串名,cin指出去哪里查找输入,另外也没有指出字符串长度的参数,string对象将根据字符串长度自动调整大小。
4、结构
(1)结构简介
● 结构是一种比数组更灵活的数据格式,可以存储多种数据类型(比如把int、float、double一起存储),从而将数据的表示合并到一起,同时,它也是类(oop)的基石。
(2)结构的声明
● 创建结构包含两点,定义结构描述并标记能够存储在结构中的各种数据类型;
Ex:struct black
{
char name[20];
float volume;
double price;
};
● struct关键字,表明这是一种结构;
● black是这种数据格式的名称,新类型名为black;
● 大括号内的是结构的数据类型列表,每个列表项都是一条声明语句;
● 列表中的每一项都被称为结构成员。
(3)创造结构变量
● 结构的创建与基本类型名相同;
Ex: black cat
● cat的类型为black;
● 可以使用成员运算符,来访问各个成员,并且按照常规类型变量那样来使用它们且像通过索引访问数组元素一样;
Ex:cat.name;(相当于是char类型的变量)
● 结构的位置:结构声明的位置在mian()函数外叫做外部声明,可被其后面的函数使用,而内部声明只能被该声明所属的函数使用;
● 结构的初始化:
a、结构的初始化同数组相同,使用逗号分隔值列表,并将这些值用花括号括起来;
b、可将每个结构成员看作是相应类型的变量;
c、若大括号内未包含任何东西,各成员都将被设置为零;
d、可将列表初始化用于结构,且等号可选;
e、可将每个结构成员都初始化为适当的类型数据;
f、不允许缩窄转换。
● 将string类做为结构成员,一定要让结构能够访问名称空间std,可使用using编译指令,也可以使用“::”。
● 结构的其他属性:
a、结构可以作为参数传递给函数,也可以让函数返回一个结构,还可以使用赋值运算符将结构赋值给另一个同类型的结构;
b、可以同时完成结构定义和创建结构变量的工作,只需将变量名放在结束括号的后面,甚至,可以初始化以这种方式创建的变量。
Ex:struct black
{
char name[20];
double price;
}cat, dog;
或:
Struct black
{
char name[20];
double price;
}cat
{
Tom,
35.5
};
● 结构数组:
结构数组的创建方法和基本类型数组方法完全相同;
Ex:black gifts[100]是一个包含了100个black结构的数组,其中数组的每个元素都是black结构的对象;
● 结构中的位字段:
C++允许制定占用特定位数的结构成员,字段类型应该为整型或枚举,接冒号,接一个制定使用位数的数字。
5、共用体
共用体是一种数据格式,能存储不同的数据类型,但只能同时存储一种类型;
● 共用体的声明:
Ex:union one4all
{
in int_val;
long long_val;
double double_val;
};
● 共用体可以在条件不同的时间,存储不同的变量(要么存储int,要么存储long,要么存储double);
● 共用体的长度为其最大成员长度;
● 共用体的主要用途是当数据使用两种或多种格式时,可以节省空间;
● 匿名共用体没有名称,其成员将成为位于相同地址处的变量。
6、枚举
(1)C++枚举提供了另一种创建符号常量的方式,可代替const,还允许定义新类型,但必须按严格的限制进行;
Ex:enum spectrum{red, orange, yellow, green, blue, violet, indigo, ultraviolet};
它完成了以下两项工作:
a、 让spectrum成为了新的类型(enum)名称;
b、 将red、orange等作为符号常量,对应数值0~7,这些常量叫做枚举常量。
● 可以使用枚举声明枚举变量:
Ex: spectrum band;
● 在不进行强制转换的情况下,只能讲定义枚举时使用的枚举量赋给这种枚举变量.
Ex:band = red; √
band = 2000; ×
● 对于枚举制定以了赋值运算符,没定义算术运算符;
● 枚举量是整型,可被提升为int类型,但int类型不能自动转换为枚举类型;
● 如果int值是有效的(如:spectrum中对应的0~7)则可以通过强制类型转换,将它赋值给枚举变量;
Ex: band = spectrum(3)等价于 band = green;
(2)设置枚举量的值
● 可以使用赋值运算符来显式地设置枚举量的值(或其中的一些枚举量的值)且制定的值必须是整数;
● 不可以创建多个值相同的枚举量。
(3)枚举的取值范围
● 枚举取值范围上限为大于枚举量最大值的最小2的幂减1;
● 枚举取值范围下限分为两种情况,若最小值不小于0,则下限为0,另一种情况是,最小值小于0,其下限为小于枚举量最小值的最小2的幂减1.
7、指针和自由存储空间
计算机程序在存储数据时必须跟踪3种基本属性:
● 信息存储在何处?
● 存储的值为多少?
● 存储的信息是什么类型?
(1)什么是指针?
● 指针是一个变量,其存储的是值的地址,而不是值本身;
● 使用”&”取地址符,来获得常规变量的地址(使用常规变量时,值是定的量,地址是派生量,使用指针的时候正好相反);
(2)指针与C++基本原理
oop强调在运行阶段进行决策,传统的过程性编程更强调在编译阶段进行决策,oop就想修理电脑一样根据问题来选择修理工具,而传统的过程性编程更像是提供一堆工具,让你来解决问题。
所以,oop使程序更灵活,在C++中,使用new关键字来请求正确数量的内存,使用指针来跟踪新分配的内存的位置。
(3)指针的表示
● 指针名表示的是地址;
● *运算符被称为间接值运算符或解除引用运算符;
● *运算符运用于指针,可以得到改地址值出存储的值;
(C++根据上下文来确定*所指的是乘法运算符还是解除引用运算符)
Ex: 假设manly是一个指针,则manly表示的是一个地址;
* manly对manly指针解除引用,表示的是存储在manly地址处的值;
* manly与常规int变量等效;
● int变量与指针变量不过是同一枚硬币的两面:
Ex:int dates = 30;
int * p_dates = &dates;
常规变量dates表示值,并使用&运算符来获得地址;
P_dates表示地址,并使用*运算符来获得值;
* p_dates 等价于 dates;
P_dates 等价于 &dates;
(4)声明和初始化指针
a、计算机需要跟踪指针指向的值的类型,所以指针声明必须制定指针指向的数据类型;
ex:int * p_dates
● p_dates为指针变量,表示的是地址;
● p_dates指向int类型;
● p_dates的类型是指向int类型的指针(或int *,在c++中int *是一种复合类型,是指向int的指针);
● * p_dates类型为int类型,而不是指针。
b、可以在声明语句中初始化指针,在这种情况下,被初始化的是指针,而不是它指向的值;
ex: int * pt = &higgens;
将pt(而不是*pt)的值设置为&higgens。
(5)指针的危险性
C++创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向数据的内存,所以,一定要在对指针应用解除引用运算符之前,将指针初始化为一个确定的,适当的地址。
(6)指针和数字
指针不是整型,所以不能简单地将数字赋给指针;
Ex: int * pt;
Pt = 0xB8000000;
C语言允许这样的赋值,但是在C++中,编译器将显示类型不匹配;
要将数字值作为地址使用,应该通过强制类型转换将数字转换为适当的地址类型:
Ex:int * pt
Pt = (int*)0xB800000;
注意:pt是int类型地址,并不意味pt是int类型。
(7)使用new来分配内存
常规变量在编译阶段分配有名称的内存储存值;
指针在运行阶段分配未命名的内存以存储值,而这种分配使用new关键字来完成;
Ex: int * pn = new int;
new int告诉程序,需要适合的存储int的内存;
new运算符根据类型来确定需要多少字节的内存,然后找到这样的内存,并返回其地址,将地址赋给pn;
● 在程序中new的使用格式如下:
typeName * pointerName = new typeName;
● 变量被存储在栈中;
● new出来的内存被分配在堆(自由存储空间)中。
(8)释放内存
C++中使用delete关键字释放内存,用法是在delete后面加上指向内存块的指针;
Ex: int * ps = new int;
… …
delete ps;
● delete将释放ps指向的内存,但不会删除ps本身;
● new和delete要配对使用,否则将发生内存泄漏;
● 不要释放同一个内存块两次;
● 只能用delete来释放new分配的内存;
● 对空指针使用delete是安全的。
(9)使用new创建动态数组
对于小型数据对象(指为数据分配的内存块)而言,使用变量无疑是更简单发方法,但对于大型数据对象,应该使用new。
● 静态联编:在编译阶段为数组分配内存;
● 动态联编:在运行阶段创建数组;
A、如何用new创建数组及使用指针访问数组元素:
a、使用new创建动态数组:
将数组的元素类型、元素数目告诉new即可;
Ex: int * psome = new int[10];
a、new运算符返回数组的第一个元素地址,赋给指针;
b、对于使用new创建的数组,应使用delete [] 来释放;
c、为数组分配内存的通用格式如下:
typeName * pointerName = new typeName[num_elements];
new可以确保内存块足够存储num_elements个类型为typeName的元素,pointerName指向数组第一个元素。
B、使用动态数组
Ex:int * psome = new int[10];
使用动态数组,只要把指针当做数组名使用即可(C和C++中数组和指针的处理方式相似,所以数组和指针基本等价);
(10)指针、数组和指针算术
● 指针与数组之间的转换:
arrayName[i] becomes *(arrayName + i);
pointerName[i] becomes *(pointerName + i);
ex:stacks[1]等价于*(stacks + 1);
● 区别1:可以修改指针的值,而数组名是常量;
● 区别2:对数组应用sizeof运算符获得的是数组长度,对指针应用sizeof得到的是指针的长度。
(11)指针小结
A、声明指针:
typeName * pointerName
B、给指针赋值:
应该将内存地址赋给指针,可以对变量名应用&运算符来获得被命名的内存地址,new返回未命名的地址;
C、对指针解除引用
对指针解除引用即获得指针指向的值,使用*来对指针解除引用;
绝对不要对未被初始化适当地址的指针解除引用;
D、区分指针和指针指向的值
若pt是指向int的指针,则*pt不是指向int的指针,而是完全等同于一个int类型的变量,pt才是指针;
E、数组名
多数情况下,C++将数组名视为数组的第一个元素地址;
F、指针算术
● 指针相加:C++允许将指针和整数相加,加1的结果等于在原来的地址上加上指针所指向的类型在系统中的字节数;
● 指针相减:相减只发生在指向同一个数组(或指向超出数组结尾的一个位置)时运算才有意义,结果是两个元素间的间隔;
G、数组的静态、动态联编
● 使用数组声明来创建数组时,将采用静态联编,数组长度在编译时设置;
● 使用new[]运算符创建数组是,将采用动态联编,数组长度在运行时设置;
F、数组表示法和指针表示法
使用[]数组表示法等同于对指针解除引用;
Ex:tacos[0] means *tacos
tacos[3] means *(tacos + 3)
(12)指针和字符串
在cout和多数C++表达式中,char数组名、char指针、引号括起来的字符串常量,都被解释为字符串第一个字符的地址;
● 有些编译器将字符串字面值视为只读常量,如果试图修改它们,将导致系统错误;
● 使用const关键字,意味着可以访问字符串,但不能修改它们;
● 有些编译器只使用字符串字面值的一个副本来标识程序中所有的该字面值;
● 在将字符串读入程序时,应使用已分配的内存地址;
● 一般来说,给cout提供一个指针,它将打印地址,但如果指针的类型为char *,则cout将显示指向的字符串,其他的类型必须强制转化才会显示字符串;
● 将数组赋值给指针,并不会赋值字符串,而只会复制地址,这样两个指针将指向相同的内存地址和字符串;
● 要获得字符串副本需要两步:
a、需要分配足够的内存:
ex: string animal = “cat”;
char * ps = new char[strlen(animal) +1];
使用strlen确定字符串长度,加1,获得包含空字符时的长度,然后使用new获得足够存储的空间;
b、使用库函数strcpy()将数组中的字符串复制到新分配的空间中;
● strcpy()接受两个参数,第一个目标地址,第二个是要复制的字符串地址;
Ex:strcpy(ps, animal)
● strncpy()接受第三个参数,即要复制的最大字符数,但要注意,如果该函数在到达字符串结尾之前,目标内存已经用完,则它将不会添加空字符。
(13)使用new创建动态结构
A、将new用于结构由两部分组成:
● 创建结构;
● 访问其结构成员;
B、使用new创建结构:
Ex:struct thing
{
int good;
int bad;
};
ting pen = {21, 32};
thing * pt = &pen;
C、创建动态结构时,不能使用句点成员运算符用于结构名,必须使用箭头成员运算符(->)来访问其成员。
● 若结构标识符是结构名,则使用句点成员运算符;
若是指向结构的指针,则使用箭头成员运算符;
● 另一种方法是,若ps是指向结构的指针,则*ps是被指向的值,即结构本身,因此可以使用(*ps).Name来访问。