C++ 语言提供了两种类似于 vector 和迭代器类型的低级复合类型——数组和指针。
数组定义中的类型名可以是内置数据类型或类类型;除引用之外,数组元素的类型还可以是任意的复合类型。没有所有元素都是引用的数组。
显式初始化数组元素:int ia[array_size] = {0, 1, 2};
如果没有显式提供元素初值,则数组元素会像普通变量一样初始化
- 在函数体外定义的内置数组,其元素均初始化为 0
- 在函数体内定义的内置数组,其元素无初始化
- 不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函
数,则必须为该数组的元素提供显式初始化。
字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。
char ca1[] = {'C', '+', '+'}; // no null char ca2[] = {'C', '+', '+', '\0'}; // explicit null char ca3[] = "C++"; // null terminator added automatically
指针是指向某种类型对象的复合数据类型,是用于数组的迭代器:指向数组中的一个元素。指针保存的是另一个对象的地址:
string s("hello world"); string *sp = &s; // sp holds the address of s
现代 C++程序采用 vector类型和迭代器取代一般的数组、采用 string 类型取代 C 风格字符串。
如果必须分开定义指针和其所指向的对象,则将指针初始化为 0。因为编译器可检测出 0 值的指针,程序可判断该指针并未指向一个对象。
- C++ 提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址。void* 表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型。不允许使用 void* 指针操纵它所指向的对象。
- C 语言中继承下来的预处理器变量 NULL,该变量在 cstdlib 头文件中定义,其值为 0。
C++ 语言中,指针和数组密切相关。特别是在表达式中使用数组名时,该名字会自动转换为指向数组第一个元素的指针:
int ia[] = {0,2,4,6,8}; int *ip = ia; // ip points to ia[0]
两个指针减法操作的结果是标准库类型(library type)ptrdiff_t的数据。与 size_t 类型一样,ptrdiff_t 也是一种与机器相关的类型,在 cstddef 头文件中定义。size_t 是 unsigned 类型,而 ptrdiff_t 则是 signed 整型。
C++ 语言强制要求指向 const 对象的指针也必须具有 const 特性:
const double pi = 3.14; double *ptr = π // error: ptr is a plain pointer const double *cptr = π // ok: cptr is a pointer toconst
允许把非 const 对象的地址赋给指向 const 对象的指针。
假设给出以下语句:
typedef string *pstring;
const pstring cstr;
声明 const pstring 时,const 修饰的是 pstring 的类型,这是一个指针。因此,该声明语句应该是把 cstr 定义为指向 string 类型对象的 const 指针,这个定义等价于:
// cstr is a const pointer to string
string *const cstr; // equivalent to const pstring cstrC++ 语言通过(const)char*类型的指针来操纵 C 风格字符串。
const char *cp = "some value"; while (*cp) { // do something to *cp ++cp; }
17 . cstring 是 string.h 头文件的 C++ 版本,而 string.h 则是 C 语言提供的标准库。
18 . 使用标准库类型 string,除了增强安全性外,效率也提高了,因此应该尽量避免使用 C 风格字符串。
19 . 每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆。C 语言程序使用一对标准库函数malloc 和free 在自由存储区中分配存储空间,而 C++ 语言则使用 new和 delete表达式实现相同的功能。
20 . 动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数实初始化;如果数组元素是内置类型,则无初始化:
string *psa = new string[10]; // array of 10 empty strings
int *pia = new int[10]; // array of 10 uninitialized ints
int *pia2 = new int[10] (); // array of 10 uninitialized ints
21 . 如果我们在自由存储区中创建的数组存储了内置类型的 const 对象,则必须为这个数组提供初始化:
// ok: value-initialized const array
const int *pci_ok = new const int[100]();
22 . 之所以要动态分配数组,往往是由于编译时并不知道数组的长度:
size_t n = get_size(); // get_size returns number of elements needed
int* p = new int[n];
for (int* q = p; q != p + n; ++q)
/* process the array */ ;
23 .动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。如果不再需要使用动态创建的数组,程序员必须显式地将其占用的存储空间返还给程序的自由存储区。C++ 语言为指针提供 delete [] 表达式释放指针所指向的数组空间:
delete [] pia;
24 .
int ia[3][4] = { /* 3 elements, each element is an array of size 4*/
{0, 1, 2, 3} , /* initializers for row indexed by 0*/
{4, 5, 6, 7} , /* initializers for row indexed by 1*/
{8, 9, 10, 11} /* initializers for row indexed by 2*/
// explicitly initialize only element 0 in each row
int ia[3][4] = {{ 0 } , { 4 } , { 8 } };
};
25 .
int ia[3][4]; // array of size 3, each element is an array of
ints of size 4
int (*ip)[4] = ia; // ip points to an array of 4 ints
ip = &ia[2]; // ia[2] is an array of 4 ints
int *ip[4]; // array of pointers to int
int (*ip)[4]; // pointer to an array of 4 ints