文章目录
数组
数组的定义和初始化
- 数组的维数只能包含整型字面值常量、枚举常量(第 2.7 节)或者用常量表达式初始化的整型 const 对象
- 非 const 变量以及要到运行阶段才知道其值的 const变量都不能用于定义数组的维数
例子:
const unsigned buf_size = 512, max_files = 20;
int staff_size = 27; // nonconst
const unsigned sz = get_size(); // const value not known until run time
char input_buffer[buf_size]; // ok: const variable
string fileTable[max_files + 1]; // ok: constant expression;常量表达式
double salaries[staff_size]; // error: non const variable
int test_scores[get_size()]; // error: non const expression
int vals[sz]; // until run time
初始化数组元素
- 在定义数组时,可为其元素提供一组用逗号分隔的初值,这些初值用花括号{}括起来,称为初始化列表,
int ia[3] = {0, 1, 2};
- 如果没有显式提供元素初值:
- 在函数体外定义的内置数组,其元素均初始化为 0
- 在函数体内定义的内置数组,其元素无初始化
- 不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显式初始化
- 如果维数大于列出的元素初值个数,则只初始化前面的数组元素;剩下的其他元素,若是内置类型则初始化为 0,若是类类型则调用该类的默认构造函数进行初始化
- 字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化;
- 要注意这两种初始化形式并不完全相同,字符串字面值包含一个额外的空字符(null)用于结束字符串
char ca1[] = {'C', '+', '+'}; // no null
char ca2[] = {'C', '+', '+', '\0'}; // explicit null
char ca3[] = "C++"; // null terminator added automatically
- ca1 的维数是 3,而 ca2 和 ca3 的维数则是 4
- 注意:与 vector 不同,一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另一个数组
指针
- 指针的概念很简单:指针用于指向对象,具体来说,指针保存的是另一个对象的地址
- 每个指针都有一个与之关联的数据类型,该数据类型决定了指针所指向的对象的类型。例如,一个 int 型指针只能指向 int 型对象
指针变量的定义
vector<int> *pvec; // pvec can point to a vector<int>
int *ip1, *ip2; // ip1 and ip2 can point to an int
double dp, *dp2; // dp2 is a ponter, dp is an object: both type double
指针可能的取值
- 一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一对象;或者是 0 值
- 若指针保存 0 值,表明它不指向任何对象
int ival = 1024;
int *pi = 0; // pi initialized to address no object
int *pi2 = & ival; // pi2 initialized to address of ival
int *pi3; // ok, but dangerous, pi3 is uninitialized
pi = pi2; // pi and pi2 address the same object, e.g.ival
pi2 = 0; // pi2 now addresses no object
- 避免使用未初始化的指针
- 使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据,引起程序崩溃
- 如果必须分开定义指针和其所指向的对象,则将指针初始化为 0。因为编译器可检测出 0 值的指针,程序可判断该指针并未指向一个对象
void* 指针
- C++ 提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址:
double obj = 3.14;
double *pd = &obj; // ok: void* can hold the address value of any data pointer type
void *pv = &obj; // obj can be an object of any type
pv = pd; // pd can be a pointer to any type
- void* 表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型
- void* 指针只支持几种有限的操作:
- 与另一个指针进行比较;
- 向函数传递void* 指针或从函数返回 void* 指针;
- 给另一个 void* 指针赋值
- 不允许使用void* 指针操纵它所指向的对象
指针操作
- 解引用可访问它所指的对象,
*sp;
- 修改指针所指对象的值,
*sp = "goodbye";
- 修改指针 sp 本身的值,使 sp 指向另外一个新对象:
sp = &s2;
指针和引用的比较
- 引用总是指向某个对象:定义引用时没有初始化是错误的,只是一个别名
- 引用一经初始化,就始终指向同一个特定对象
- 给引用赋值修改的是该引用所关联的对象的值,而指针是使其与另一个对象关联
指向指针的指针
- 指针本身也是可用指针指向的内存对象:
int ival = 1024;
int *pi = &ival; // pi points to an int
int **ppi = π // ppi points to a pointer to int
int *pi2 = *ppi; // ppi points to a pointer
int value = **ppi;
注意:对**ppi解引用,先得到指针(int *),第二次解引用才得到具体数值
使用指针访问数组元素
int ia[] = {0,2,4,6,8};
int *ip = ia; // ip points to ia[0]
- 数组名就是数组中第一个元素的地址
指针的算术操作
- 在指针上加上(或减去)一个整型数值 n 等效于获得一个新指针,该新指针指向指针原来指向的元素之后(或之前)的第 n 个元素
- 注意:在不同类别下,指针加1或减1,移动的位数是不同的。int类型,将移动4位,char类型,只会移动1位
- 只要两个指针指向同一数组或有一个指向该数组末端的下一单元,C++ 还支持对这两个指针做减法操作:
ptrdiff_t n = ip2 - ip; // ok: distance between the pointers
- 结果是 4,这两个指针所指向的元素间隔为 4 个对象
- 结果是标准库类型(library type)
ptrdiff_t
的数据,与size_t
类型一样,ptrdiff_t
也是一种与机器相关的类型,在 cstddef 头文件中定义。 - size_t 是unsigned 类型,而 ptrdiff_t 则是 signed 整型
计算数组的超出末端指针
const size_t arr_size = 5;
int arr[arr_size] = {1,2,3,4,5};
int *p = arr; // ok: p points to arr[0]
int *p2 = p + arr_size; // ok: p2 points one past the end of arr
C++ 允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作
遍历数组:
- 已知数组长度
const size_t arr_sz = 5;
int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };
// pbegin points to first element, pend points just after the last
for (int *pbegin = int_arr, *pend = int_arr + arr_sz;
pbegin != pend; ++pbegin)
cout << *pbegin << ' '; // print the current element
- 数组长度未知(字符数组)
char a[] = "hello world";
for(int i = 0;a[i] != '\0';++i)
cout << a[i] << ' ';
cout << endl;
指针和 const 限定符
指向 const 对象的指针
- 如果指针指向const 对象,则不允许用指针来改变其所指的 const 值,但可以改变指针,使得指针指向其他元素(const 或非const):
const double *cptr; // cptr may point to a double that is const
const double pi = 3.14;
cptr = π //ok
double PI = 3.14;
cptr = &PI; //ok
PI = 3.1; //ok
*cptr = 3.1; //error, 不能通过指针修改值
- 把一个 const 对象的地址赋给一个普通的、非 const 对象的指针也会导致编译时的错误:
const double pi = 3.14;
double *ptr = π // error: ptr is a plain pointer
const double *cptr = π // ok: cptr is a pointer to const
const 指针
- 本身的值不能修改,但其指向的元素的值可以改变
int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer
*curErr = 0; // ok: reset value of the object to which curErr is bound
int nextNumb = 1;
curErr = &nextNumb; // error, can't change
指针和 typedef
typedef string *pstring;
const pstring cstr;
- cstr是什么类型?
- 第一行,
typedef
将string *
定义为pstring
,那么是否意味着有:
const string *cstr
故而,cstr代表了指向const string对象的指针,这是错误的!!!
2. 需要明确的是,const修饰的是pstring(指针),因此应为:
string const *cstr
typedef不是简单的文本替换!!!
C 风格字符串
- 头文件:
#include <cstring>
- 注意:
- strlen是通过字符串结束符null来判断长度的,若字符串没有null,该函数不能正常工作
- 传递给标准库函数 strcat 和 strcpy 的第一个实参数组必须具有足够大的空间存放新生成的字符串
创建动态数组
动态数组的定义
- 动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new 表达式返回指向新分配数组的第一个元素的指针
int *pia = new int[10];
int n;
cin >> n;
int *p = new int[n]; //ok
- 此 new 表达式分配了一个含有 10 个 int 型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针 pia
- new 表达式需要指定指针类型以及在方括号中给出的数组维数,该维数可以是任意的复杂表达式
- C++ 虽然不允许定义长度为 0 的数组变量,但明确指出,调用 new 动态创建长度为 0 的数组是合法的
初始化
- 态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数)实现初始化;如果数组元素是内置类型,则无初始化
- 也可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化
string *S = new string[10]; //默认构造函数初始化
int *P = new int[10]; //无初始化
int *p = new int[10](); //有初始化,全为0
const 对象的动态数组
如果我们在自由存储区中创建的数组存储了内置类型的 const 对象,则必须为这个数组提供初始化:因为数组元素都是 const 对象,无法赋值
// error: uninitialized const array
const int *pci_bad = new const int[100];
// ok: value-initialized const array
const int *pci_ok = new const int[100]();
动态空间的释放
delete [] pia;
string和c语言风格字符串
- 要求 C 风格字符串的地方不可直接使用标准库 string 类型对象
- 例如:无法使用 string 对象初始化字符指针
char *str = st2; // compile-time type error
char *str = st2.c_str(); // almost ok, but not quite
使用数组初始化 vector 对象
const size_t arr_size = 6;
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
// ivec has 6 elements: each a copy of the corresponding element in int_arr
vector<int> ivec(int_arr, int_arr + arr_size);
传递给 ivec 的两个指针标出了 vector 初值的范围。第二个指针指向被复制的最后一个元素之后的地址空间
指针和多维数组
- 定义指向多维数组的指针时,千万别忘了该指针所指向的多维数组其实是数组的数组
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(指向含有4个int的数组)
ip = &ia[2]; // ia[2] is an array of 4 ints
注意:括号一定不能少!!!
int *ip[4];
int (*ip)[4];
第一个代表,ip是一个含有4个元素的数组,且这四个元素都是整数指针
第二个代表,ip是一个指针,指向一个含有4个整数的数组