4.1 数组
数组定义中的类型名可以是内置类型或者类类型,除引用之外,数组元素的类型还可以是任意的复合类型。没有所有元素都是引用的数组
4.11数组的定义和初始化
数组的维数必须用值大于1的常量表达式定义。此常量表达式只能包含整型字面值、枚举常量或者用常量表达式初始化的整型const对象。非const变量以及要到运行时才知道其值的const变量都不能用于定义数组的维数
1.显示初始化数组元素
如果没有显示提供元素初值,则数组元素会像普通变量一样初始化:
在函数体外定义的数组,其元素被初始化为0
在函数体内定义的数组,其元素无初始化
不管数组哪里定义,如果其元素类型是类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组提供显示初始化
显示初始化的数组不需要指定数组的维数值,编译器会根据列出的元素个数来确定数组长度;
int a[]={1,2,4};
2. 特殊的字符数组
字符数组既可以引用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。当使用字符串字面值来初始化创建的新数组时,将在数组中加入空字符串
3. 不允许数组直接复制和赋值
与vector不同,一个数组不能用另一个数组初始化,也不能将一个数组赋值到另一个数组,这些操作是非法的
注意:数组一经定义,就不允许再添加新元素
4.2 指针的引入
4.2.2 指针的定义和初始化
每个指针都有一个与之关联的数据类型,该数据类型决定了指针所指向的对象的类型。
4. 指针可能的取值
一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一个对象;或者是0值。
若指针保存0值,表明它不指向任何对象。未初始化的指针是无效的。直到给该指针赋值后才可使用它。
5.避免使用未初始化的指针
若果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中的存放的内容。
6.对指针进行初始化或者赋值只能使用以下四种类型的值:
(1)0值常量表达式。例如:在编译时可获得的0值得整型const对象或者字面值常量0.
(2)类型匹配的对象的地址
(3)另一对象之后的下一个地址
(4)同类型的另一个有效的指针
允许把数值0或在编译时可获得0值得const量赋给指针
由于指针的类型用于确定指针所指的对象的类型,因此初始化或者赋值时必须保证类型匹配
7. void*指针
c++提供了一种特殊的指针类型void *,它可以保存任何类型对象的地址:
void*指针只支持几种有限的操作:与另一个指针进行比较;向函数传递void *指针或者返回void *指针;给另外一个void *指针赋值。不允许使用void *指针操纵它所指向的对象。
4.2.3 指针操作
指针提供间接操纵其所指对象的功能。
2. 指针和引用的比较
引用和指针之间有两个重要的区别:第一个区别在于引用总是指向某个对象:定义引用时没有初始化是错误的。第二个重要的区别是赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向同一个特定的对象
4.2.4 使用指针访问数组元素
1.指针的算数运算
指针的算术操作只有在原指针和计算出来的新指针都指向同一个数组的元素,或者指向该数组存储空间的下一个单元才是合法的。
只要两个指针指向同一个数组或有一个指向该数组末端的下一个单元,c++支持两个指针做减法
3. 下标和指针在使用下标访问数组时,实际上是对指向数组元素的指针做下标操作。
1: int *P=&a[2];
2: int j=p[1];//实际就是a[3]3: int k=p[-2];//实际是a[0]
4.2.5指针和const限定符
const对象的指针和const指针
1.指向const对象的指针
c++强制要求指向const对象的指针也必须具有const特性
不能用void *保存const对象的地址,而必须用const void *类型的指针保存
允许把非const 对象的地址赋给const对象的指针
不能保证指向const的指针所指的对象的值一定不可以修改(应该称作自以为指向const的指针)
2. cosnt 指针
const指针—本身的值不能修改
const指针必须在定义时进行初始化
3.指向cosnt对象的const 指针
const double pi=3.1415;
const double *const pi_ptr=π
既不能修改pi_ptr所指向对象的值,也不能修改该指针的指向
4.指针和typedef
假设给出下列语句
1: typedef string *pstring;
2: const pstring cstr;
cstr是const指针.声明cosnt pstring时,cosnt修饰的是pstring类型,这是一个指针。cstr定义为指向string类型的const指针。这个定义等同于 string *const cstr;
4.3 C风格字符串
字符串字面值就是const char型的数组。
2.C风格字符串的标准库函数
传递给这些标准库函数的指针必须具有非零值,并指向以null结束的字符数组中的第一个元素
1: strlen(s) //返回s的长度,不包括字符串结束符null
2: strcmp(s1,s2) //比较字符串s1和s2是否相同。若相等,则返回0,;若s1大于s2返回正数,否则返回负数3: strcat(s1,s2) //将字符串s2接到s1后,并返回s1
4: strcpy(s1,s2) //将s2复制给s15: strncat(s1,s2,n)//将s2的前n个字符连接到s1后面,并返回s1
6: strncpy(s1,s2,n) //将s2的前n个字符复制给s1,并返回s1
4.3.1创建动态数组
数组类型的变量有三个重要的限制:数组固定长度不变,在编译时必须知道其长度,数组只在定义它的块语句内存在。
我们可以在运行时动态地分配数组。虽然数组长度是固定的,但动态分配的数组不必在编译时就知道长度,可以在运行时才确定数组长度。与数组变量不同,动态分配的数组将一直存在,直到人显示地释放它
1.动态数组的定义
动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针:
int *pia=new int[10];
new表达式需要指定指针类型和在方括号内的数组维数,该维数可以是任何复杂的表达式。
2. 初始化动态分配的数组
动态分配数组时,如果数组元素是类类型,将使用类的默认构造函数实现初始化;如果数组元素是内置类型,则无初始化;
也可以使用在数组长度后面的一对空圆括号,对数组元素做值初始化
int *pia2=new int[10]();
圆括号要求编译器做值初始化,在上面例子中,将数组元素初始化为0
对于动态分配的数组,其元素只能初始化为元素类型的默认值
3. const对象的动态数组
因为数组元素都是const对象,无法赋值。所以必须对数组进行值初始化:
const string *p=new const string[10];//string类的默认构造函数初始化数组元素
const int *pci=new const int[10]();
5.动态空间的释放
c++使用delete []表达式释放指针所指向的数组空间:
delete [] pia;
该语句回收了pia所指向的数组,它告诉编译器该指针指向的是自由存储区中的数组,并非单个对象。
4.3.2 新旧代码兼容
1.混合使用标准库类string和c风格字符串
可以使用c风格字符串对string对象进行初始化或者赋值
string类型的加法操作需要两个操作数,可以使用c风格字符串作为其中一个操作数
无法用string类型初始化字符指针
但是string类提供了一个名为c_str的成员函数,以实现我们的要求:
char *str=st2.c_str();失败
因为c_str返回的指针指向const char类型的数组,所以上述初始化失败了,这样做是为了避免修改该数组。
const char *str=st2.c_str();成功
2. 使用数组初始化vector对象
使用数组初始化vector对象必须指出用于初始化式的第一个元素以及数组最后一个元素的下一位置的地址
const size_t arr_size=6;
int int_arr[arr_size]={0,1,2,3,4,5};
vector<int> ivec(int_arr,int_arr+arr_size);
第二个指针指向被复制的最后一个元素之后的地址空间。
4.4 多维数组
2.多维数组的下标引用
如果表达式只提供了一个下标,则结果获取的元素师该行下标索引的内层数组。如ia[3][4]数组,ia[2]将获得ia数组的最后一行。
指针和多维数组
使用多维数组名,实际上将其自动转换为指向该数组第一个元素的指针
int ia[3][4];
int (*ip)[4]=ia; ip指向含有4个元素的数组
ip=&ia[2]; ia[2]是一个具有4个元素的数组
*ip是int[4]类型——即ip是一个指向含有4个元素数组的指针