《C++ Primer Plus》学习笔记——第4章 复合类型

4.1 数组

  • 编译器不会检查数组的下标是否有效,所以应该格外小心,以免出现数组越界的错误。
  • 只有在定义的时候才能使用初始化,此后就不能使用了,也不能将一个数组赋值给另一个数组。
  • 初始化数组时,提供的值可以少于数组的元素数目,其他的会被编译器初始化为0。
  • C++11可以初始化数组时省略等号
#include 
using namespace std;

int main() {
    int arr1[5] = {1, 3};
    cout << arr1[3] << endl;
    int arr2[5]{1, 3, 5};
    cout << arr2[2] << endl;
    return 0;
}
输出
0
5

4.2 字符串

  • 字符串时存储在内存中的连续字节中的一系列字符,C风格的是char类型的数组,C++提供了另外一种string类库。
  • 使用C风格字符串时,需要注意最后结尾为空字符,数组长度也要在字符串长度上加一以存储空字符。
  • 任意两个引号括起的字符串会合并为一个字符串常量。
  • 可以使用strlen计算字符串的长度,strlen只计算可见字符,不会把空字符计算在内。
  • cin使用空白(空格、制表符和换行符)来确定字符串的结束位置。
  • geline函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。
  • get的一种变体和getline类似,不同的是会读取换行符到队列中,所以重复使用get会一直卡在换行符那,可以使用cin.get()来读取下一个字符来跳过换行符。
  • C++常用指针来处理字符串,未经过初始化的字符数组的内容是未定义的。
  • C++可以创建wchar_t、char16_t和char32_t类型的字符串,和前面的使用相同,只需要制定L、u和U关键字。

4.3 结构简介

  • 同一个结构可以存储多种类型的数据,且可以创建结构数组
  • 创建结构可以分为两步,一是创建结构描述,二是根据结构描述创建结构变量。
#include 
using namespace std;

struct student {
    string name;
    int age;
    double record;
};

int main() {
    student s1;
    s1.name = "zhangsan";
    s1.age = 14;
    s1.record = 98.5;
    cout << s1.name << endl;
    cout << s1.age << endl;
    cout << s1.record << endl;
    return 0;
}
  • C++允许在声明结构变量时省略关键字struct。
  • 结构声明可以分为内部声明和外部声明,内部声明在函数内部,只能被声明所属的函数使用;外部声明在函数外面,能够被其后面的任何函数使用。
  • C++不提倡使用外部变量,但提倡外部结构声明。
  • 结构可以使用列表初始化,如果大括号内没有包含任何东西,则会被设置成零。
student s2 {"lisi", 34, 94.2};
  • 可以同时完成定义结构和创建结构变量的工作,但是将结构定义和变量声明分开更便于阅读和理解。
#include 
using namespace std;

struct student {
    string name;
    int age;
    double record;
} s1;

int main() {
    s1.name = "zhangsan";
    s1.age = 14;
    s1.record = 98.5;
    cout << s1.name << endl;
    cout << s1.age << endl;
    cout << s1.record << endl;
    return 0;
}
  • C++也支持制定占用特定位数的结构成员,字段的类型应该为整型或枚举类型。
  • 结构体所占用的内存是各个元素占用内存的总和。
struct torgle_register
{
	unsigned int SN: 4;
	unsigned int : 4;
	bool goodIN : 1;
	bool goodTorgle : 1
}

4.5 共用体

  • 共用体是一种数据格式,能够存储不同的数据类型,但只能同时存储其中的一种类型。
#include 

using namespace std;

int main() {
    union un1 {
        int int_val;
        double d_val;
        bool b_val;
    };
    un1 utest;
    utest.int_val = 13;
    cout << utest.int_val << endl;
    utest.d_val = 5.4;
    cout << utest.d_val << endl;
    utest.b_val = true;
    cout << utest.b_val << endl;
    return 0;
}
  • 共用体所占用的内存是各元素中占用内存最大的值。
  • 共用体的用途之一是当数据项使用两种或更多格式时,可节省空间。

4.6 枚举

  • C++中的enum工具可以创建枚举,提供了另一种创建符号常量的方式,这种方式可以代替const。
enum color {red, blue, green};

4.7 指针和自由存储空间

  • 指针是一个变量,其存储的是值的地址。一个变量的地址可以使用地址运算符获取(&)。
  • 使用常规变量的时候,值是指定的量,而地址是派生量,指针策略是C++内存管理编程理念的核心。
  • 处理存储数据的新策略刚好相反,将地址视为指定的量,而将值视为派生量,一种特殊的变量——指针,用于存储值的地址,因此指针表示的就是地址。
  • *运算符被称为间接值或解除引用运算符。
  • int* 是一种类型——指向int的指针。
  • 对每个指针变量名都需要使用一个*,如下所示,是声明了一个指针和一个int类型的变量。
int *p1, p2;
  • 指针是复合类型,char类型和long类型占用空间不同,但是指向他们的指针却相同,因为地址长度是固定的。
  • C++中创建指针时,计算机会分配用来存储地址的内存,但不会分配用来村粗指针所指向的数据的内存。
  • 一定要在指针使用解除引用运算符(*)之前将指针初始化为一个确定的、适当的地址。
  • 要将数字值作为地址来使用时,应通过强制类型转换符将数字转换成适当的地址类型。
int *pt;
pt = (int *)0xB8000000;
  • C语言中,可以使用库函数malloc来分配内存,C++中仍然可以这样做,C++中有一种更好的方式——new运算符。
  • 只需要告诉new,需要为哪种数据类型分配内存,new将找到一个长度正确的内存块,并返回该内存块的地址,所以new返回的是地址,可以用指针变量接收。
  • new从被称为堆或自由存储区的内存区域分配内存。
  • new通常会引发异常,在较老的实现中,new将返回0,在C++中,值为0的指针被称为空指针。
  • new请求的内存需要使用delete函数来释放,如果不释放,可能会造成内存泄露。
  • 只能使用delete来释放new申请的内存,对空指针使用delete是安全的。
  • 在编译时给数组分配内存称为静态联编,意味着数组的存储空间在编译时已经分配。但使用new时,如果在运行阶段需要数组,则创建它,这被称为动态联编,这种数组被称为动态数组。
int *arr = new intp[10];
delete [] arr;
  • 不要使用delete释放同一个内存块两次
  • 不能使用sizeof运算符来确定动态分配的数组包含的字节数。
  • C和C++内部使用指针来处理数组,数组名可以表示数组的首地址。

4.8 指针、数组和指针算术

  • 指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。C++将数组名解释为指针,可以通过该指针来访问数组中的元素。比如对于int类型的指针,指针加一,则内存位置移动4字节,表示数组的下一个元素。
  • 对于数组arr[1],C++编译器可以看做*(arr+1),与变量名不同的是,变量名不可以修改,而指针可以修改。
  • 另一个区别是,使用sizeof计算数组,得到的是数组的长度,而使用sizeof计算指针是获取指针的长度。
  • 还有一个区别就是对于int *arr = new int[5];,&arr[0]是一个4字节的地址,而&arr则是一个20字节的首地址,前者加一是移动4字节,后者加一是移动20字节。对数组名使用指针就是后者。
  • 使用解除引用运算符可以获取指针所指向地址的值。
  • 对于字符串数组,如果cout提供一个字符的地址,则会从该字符开始打印,直到遇到空字符为止。
  • 在C++中,用引号括起的字符串和数组名一样,也是第一个元素的地址。
  • 对于字符串的接收,可以使用足够大的char数组来接收输入,不要使用字符串常量和未被初始化的指针来接收输入,为避免这个问题,可以是用std::string。字符串常量被const修饰,不能被覆盖,而未初始化的指针则会导致写入的值会覆盖未知的内存,修改未知内存处的值。
  • 如果给cout提供一个指针,它将打印地址,如果这个指针是char*类型的则会被当做字符串处理。
  • 对于数组的赋值,应使用strcpy()或strncpy(),而不是赋值运算符来将字符串赋给数组。
  • 如果结构标识符是结构名,则使用句点运算符,如果标识符是指向结构的指针,则使用箭头运算符。
  • 根据用于分配内存的方法,C++有3种管理数据内存的方式:自动存储、静态存储和动态存储。
  • 函数内部定义的变量使用自动存储空间,被称为自动变量,函数调用时产生,调用结束后消亡。
  • 静态存储是整个程序执行期间都存在的存储方式,使变量成为静态的方式有两种:一种是函数外面定义,另一种是使用const修饰符。
  • 动态存储,使用new创建的变量。使用new创建的需要使用delete释放,否则会发生内存泄露。如果没有调用delete,则即使包含指针的内存由于作用域规则和对生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在。实际上,将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效,这将导致内存泄露,被泄露的内存将在程序的整个生命周期内部不可使用,这些内存被分配出去将无法回收。

4.9 类型组合

  • 指针、数组、结构可以结合使用

4.10 数组的替代品

  • 模板类vector是一种动态数组,可以设置vector的长度,在其后添加元素
  • 实际上vector确实是使用new和delete来管理内存的,这种长度的变化是自动完成的。
  • vector相比数组来说,效率稍低,如果是长度固定的数组,可以使用array。array与数组一样,长度固定,也使用栈
  • array的好处是可以将一个array赋值给另一个array,而数组则需要复制。

你可能感兴趣的:(C++语言,c++,数据结构,算法)