说明
看《C++ Primer Plus》时整理的学习笔记,部分内容完全摘抄自《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,张海龙 袁国忠译,人民邮电出版社。只做学习记录用途。
目录
复合类型是基于基本整型和浮点类型创建的,影响最为深远的复合类型是类,除类外,C++ 还支持几种更普遍的复合类型,它们都来自 C 语言,例如:数组、结构、指针等。
4.1 数组
数组能够存储多个同类型的值,计算机在内存中依次存储数组的各个元素。数组声明应包含三点:元素类型、数组名、数组中的元素总数,如下:- //数组声明
- typeName arrayName[arraySize];
复制代码 其中的表达式 arraySize不能是变量,它必须是整型常量或const值,也可以是常量表达式(如8*sizeof(int)),即表达式中所有值在编译时都是已知的。C++ 标准模板库(STL)提供了一种数组替代品,模板类vector,C++11 新增了模板类array,这些替代品比内置复合类型数组更复杂、更灵活,这将在后面章节介绍。声明数组后,数组中的元素总数也可以使用以下方式计算出来:- //数组中的元素总数
- arraySize = sizeof(arrayName)/sizeof(typeName);
复制代码 4.1.1 数组访问
使用[index]访问索引为 index的数组元素,索引从 0 开始,最后一个元素的索引比数组长度小 1。- //声明长度为12的short数组
- short months[12];
- //访问它的第1个元素
- months[0];
- //访问它的最后一个元素
- months[11];
复制代码 注意:编译器不会检查索引是否有效,例如可以访问months[101]或者给它赋值,编译器并不会指出错误(有时会给个警告,不是以错误的形式指出),但是程序运行后,这种赋值可能破坏数据或代码,也可能导致程序异常终止,因此需人为确保程序使用有效的索引值。
4.1.2 数组初始化及赋值
只有在定义数组时才能使用初始化,也不能将一个数组赋给另一个数组,但可以逐元素赋值。初始化数组时,提供的值可以少于数组的元素数目,剩余的元素编译器会自动初始化为 0。- //定义时初始化全部元素
- int arr1[4] = {3, 6, 8, 10};
- //定义时只指定第一个值,剩余元素初始化为0
- int arr2[4] = {3};
- //定义时全部初始化为0
- int arr3[4] = {0};
- //定义时指定全部元素,让编译器自动统计元素个数
- int arr4[] = {3, 6, 8, 10};
- //C++11初始化方式:可省略等号
- int arr5[4]{3, 6, 8, 10};
- //C++11初始化方式:默认初始化为0
- int arr6[4] = {};
- int arr7[4]{};
复制代码 注意:使用大括号进行列表初始化时禁止缩窄转换。
4.2 字符串
C++ 处理字符串的方式有两种:第一种来自 C 语言,常被称为 C-风格字符串,第二种是下一节将介绍的string类。
C-风格字符串将字符串存储在char数组中,并以空字符结尾。空字符被写作\0,其 ASCII 码为 0,用来标记字符串的结尾。很多处理字符串的函数(如cout输出字符串)都逐个处理字符串中的字符,直到到达空字符为止,无论是否超过了char数组的实际大小。
4.2.1 C - 风格字符串的初始化及拼接
常用的初始化方法如下:- //逐个字符初始化,需人为加空字符
- char cat[8] = {'f','a','t','e','s','s','a','\0'};
- //使用字符串常量进行初始化,自动添加空字符
- char bird[11] = "Mr. Cheeps";
- //字符数组剩余元素自动初始化为空字符
- char boss[8] = "Bozo";
- //让编译器计算字符数组长度,长度为8
- char fish[] = "Bubbles";
- //C++11字符串初始化
- char fish[] = {"Bubbles"};
- char fish[]{"Bubbles"};
复制代码 注意:字符串常量(使用双引号)不能与字符常量(使用单引号)互换。在 ASCII 系统上,字符常量'S'只是 83 的另一种写法,但"S"不是字符常量,它表示的是两个字符(字符S和字符\0)组成的字符串,更糟糕的是,"S"实际上表示的是字符串所在的内存地址。
C++ 允许将两个用引号括起来的字符串拼接成一个,任何两个由空白分隔的字符串常量都将自动拼接成一个。下面所有输出语句都是等效的:- //输出
- cout << "Medical cotton swab.\n";
- //使用空格分隔的拼接
- cout << "Medical cot" "ton swab.\n";
- //使用换行分隔的拼接
- cout << "Medical cot"
- "ton swab.\n";
复制代码 4.7.6 内存管理
C++ 有 3 种管理数据内存的方式:自动存储、静态存储和动态存储(也称自由存储空间或堆)。
- 自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量,自动变量是一个局部变量,其作用域为包含它的代码块(代码块指包含在花括号中的一段代码)。自动变量通常存储在栈(stack)中,执行代码块时,其中的变量将依次加入到栈中,离开代码块时,将按相反的顺序自动释放这些变量,即后进先出(LIFO),这种机制使得栈的内存通常是连续的。
- 静态存储:静态变量将存在于程序的整个生命周期,而不是代码块内,使变量成为静态变量的方式有两种:在函数外面定义它、声明变量时加上关键字static。
- 动态存储:new和delete管理一个内存池,这在 C++ 中被称为自由存储空间(free store)或堆(heap)。该内存池同自动存储、静态存储是分开的,数据的生命周期完全由程序员控制,可以在一个函数中分配内存,而在另一个函数中释放它,这使得自由存储空间通常是不连续的。
C++11 新增了第 4 种方式:线程存储,这将在第 9 章学习。
4.8 指针算术和数组名
4.8.1 指针算术
C++ 允许将指针和整数相加,指针加 1 的结果等于原来的地址值加上指向的对象占用的总字节数;还可以将指针相减,获得两个指针的差,最后得到一个整数,指针减法仅当两个指针指向同一个数组时,运算结果才有意义。如下示例中假设short宽 2 字节,数组首地址为 0x0028ccf0:- //将字符串arrayName2复制到数组arrayName1中
- strcpy(arrayName1,arrayName2);
- //将字符串arrayName2添加到数组arrayName1末尾
- strcat(arrayName1,arrayName2);
复制代码 4.8.2 数组名
C++ 将数组名解释为其第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址。从数字上而言,这两个地址相同,无需区分;但从概念上特别是需要运用指针算术时,需要明白两者的区别。如下示例中假设short宽 2 字节,系统为 32 位,数组首地址为0x0028ccf0,指针变量ptr和ptrc的区别如下:
- 变量ptr的类型是short*,存储的是一个 2 字节内存块的地址,它指向的对象是short类型,记号*ptr与tacos[0]等价。
- 变量ptrc的类型是short(*)[10],存储的是一个 20 字节内存块的地址,它指向的对象是包含 10 个元素的short数组,记号*ptrc与tacos等价。
[code]//声明并初始化数组short tacos[10] = {5,2,8,4,1,2,2,4,6,8};//声明并初始化指针short *ptr = tacos;short (*ptrc)[10] = &tacos;//访问数组第三个元素cout |