现在的位置: 首页 > 综合 > 正文

C++Primer读书笔记(第四章)

2014年11月17日 ⁄ 综合 ⁄ 共 4752字 ⁄ 字号 评论关闭
文章目录

第四章数组和指针

注:现代C++程序采样vector类型和迭代器取代一般的数组、采用string类型取代C风格字符串。

4.1 数组

         数组是C++中类似标准库vector类型的内置数据结构。与vector类型相比,数组的显著缺陷在于:数组的长度是固定的,而且程序员无法知道一个给定数组的长度。数组没有获取器容量大小的size操作,也不提供push_back操作在其中自动添加元素。

4.1.1 数组的定义和初始化

         数组的位数必须用值大于等于1的常量表达式定义(整型字面值常量、枚举常量或者用常量表达式初始化的整型const对象)。如果没有显示提供元素初始值,在函数体外定义的内置数组元素均初始化为0,在函数体内定义的局部数组则无初始化(相应操作也是无意义)。

         const  unsigned array_size = 3;

         int  ia[array_size] = {0, 1, 2};         

         int  ia[] = {0, 1, 2};           //显示初始化可以不需要指定数组的维数值

         字符数组既可以用一组字符字面值初始化,也可以用一个字符串字面值进行初始化,但是要考虑其末尾的’\0’。另外注意,数组不允许直接复制和赋值。

4.1.2 数组操作

         数组支持下标操作,下标的类型是size_t。防止数组越界,没有别的方法,只有程序员自己注意和彻底测试。

4.2 指针的引入

         指针是用于数组的迭代器。指针保存的是一个对象的地址。

4.2.1 指针的定义和初始化

         string  *ps1, *ps2;                   //将ps1和ps2都声明为string类型的指针

         一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一个对象;或者是0值。若保存为0值,表面它不指向任何对象。指针只有初始化后才能使用(未初始化的指针中存放的不确定值在运行时不能被识别,会操作该内存地址中的内容,引起程序崩溃)。

         如果可能的话,除非所指向的对象已经存在,否则不要先定义指针。如果必须分开定义指针和其所指对象,则将指针初始化为0。

         对指针进行初始化或赋值只能使用一下四种类型的值:(1)字面值常量0和整型const对象0值(NULL等效,在cstdlib头文件中定义,不是std命名空间中定义);(2)类型匹配的对象地址;(3)另一个对象之后的下一地址;(4)同类型的另一个有效指针。

         int  *pi = 0;              //pi不指向任何对象,且值为0

         void*指针可以保存任何类型对象的地址,但是只支持几种有限的操作:与另一个指针进行比较;向函数传递void*指针或从函数返回void*指针;给另一个void*指针赋值。不允许使用void*指针操作它所指向的对象。

4.2.2 指针操作

         与迭代器进行解引用操作一样,*操作符将获取指针所指对象。指针的算术操作和迭代器的算术操作以相同方式实现,也有相同的约束。指针加上或者减去一个整型数值,就可以计算出指向后面或者前面的另一元素的指针值。

         ptrdiff_t= ip2 – ip1;          //ip1和ip2是两个指向同一数组或有一个指向该数组末端的下一个单元。ptrdiff_t类型,在cstddef头文件中定义,是signed整型。

         使用下标访问数组时,实际上是使用下标访问指针:

         int  *p = &ia[2];

         int  j = p[1];              //p[1]等价于:*(p + 1)

         类似vector中的end操作,允许指针指向数组或对象的超出末端的首个地址,但不允许对此地址进行解引用操作。

4.2.3 指针和const控制符

(1)指向const对象的指针(“自以为指向const的指针”,不能通过指针修改对象值)

         如果指针指向const对象,则不允许用指针来改变其所指的const值。为了保证这个特性,C++强制要求指向const对象的指针也必须具有const特性:

         const  double *cptr;

         cptr本省并不是const,允许不初始化定义或者指向其它对象。不可以把const对象的地址赋给一个非const对象的指针,若要使用void*指针,则必须使用const  void*类型。允许把非const对象的地址赋值给指向const对象的指针,但是同样不允许通过指针修改其值。

         指向const对象的指针常用于函数的形参,以确传递给函数的实际对象在函数中不因为形参而被改变。

(2)const指针(指针本身的值不能修改)

         int  *errNumb = 0;

int  *const curErr = &errNumb;          //curErr不允许在指向其它对象

(3)指向const对象的const指针(本身和所指对象都不能修改)

         const  double pi = 3.14159;

         const  double *const  pi_ptr = & pi;

(4)指针和typedef

         typedef  string *pstring;

         const  pstring cstr;              //其实可以写成:pstring  const cstr;这样更容易理解,它们都等价于string  *const  cstr;

         因为,声明const  pstring时,const修饰的是pstring类型,这是一个指针。因此,该声明语句应该是把cstr定义为指向string类型对象的const指针。

4.3 C风格字符串

4.3.1 什么是C风格字符串

C风格字符串既不能确切归结为C语言的类型,也不能归结为C++语言的类型,而是以空字符null结束的字符数组。

C语言标准库提供了一系列处理C风格字符串的库函数,要使用这些函数,必须包含头文件csting。这些标准库函数不会检查其字符串参数:

strlen(s)                      //返回s的长度,不包括null

         strcmp(s1,s2)          //比较两个字符串s1和s2,相等为0,s1大返回整数,否则为负数

         strcat(s1,s2)            //将s2连接到s1之后,返回s1

         strcpy(s1,s2)            //将s2复制给s1

         strncat(s1,s2, n)     //将s2的前n个字符连接到s1后面

         strncpy(s1,s2, n)     //将s2的前n个字符复制给s1

         永远不要忘记字符串结束符null:

         char  ca[] = {‘C’, ‘+’, ‘+’};

         cout<< strlen(ca) << endl;

         其中,ca是一个没有null结束符的字符数组,则计算结果不可预料,会计算从ca开始,直到遇到内存中内容为null的地址结束。

         传递给strcat和strcpy的第一个实参数组必须具有足够大的空间存放新生成的字符串。这样的程序很容易出错,若非使用C风格字符串,则使用strncat和strncpy更安全,因为可以适当控制连接或复制的字符个数。但是不管怎样,还是尽量使用string类型。

4.3.2 创建动态数组

         每个程序在执行时都占有一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆。C语言中使用malloc和free,C++使用new和delete来分配和释放存储空间。

         动态数组的定义:int  *pia = new int[n];          //n可以是任意复杂表达式

         初始化动态分配的数组:int  *pia = new int[n] ();           //对数组初始化为全0

         对于动态分配的数组,气元素只能初始化为元素类型的默认值,而不能像数组变量一样,用初始化列表为数组元素提供各不相同的初值。

         允许动态非配空数组,用new动态创建长度为0的数组时,new返回有效的非零指针,但是该指针不能进行解引用操作,只允许进行比较,在该指针上加减,或减去本身得0值。

         动态分配的内存空间最后必须进行释放,delete  [] pia;回收pia所指向的数组空间。

4.3.3 新旧代码的兼容

         string类提供了一个名为c_str的成员函数,以实现将string转化为对应的C风格字符串,且返回指向字符数组首地址的指针,数组末尾为null。c_str返回的指针指向的是const  char类型的数组,为了避免修改该数组。

         const  char *p_str = str.c_str();

         使用c_str返回的数组不保证一定有效,因为接下来的对str的操作可能改变str的值,那么刚才返回的数组就失效了。

         可以使用数组初始化vector对象,必须指出用于初始化的第一个元素和最后一个元素的下一位置地址:

         const  aize_t arr_size = 6;

         int  int_arr[arr_size] = {0, 1, 2, 3, 4, 5};

         vector<int>  ivec (int_arr + 1, int_arr + 4);    //用了int_arr[1],int_arr[2], int_arr[3]

4.4 多维数组

         多维数组其实就是数组的数组。

4.4.1 多维数组的初始化

int  ia[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8,9, 10, 11} };

int  ia[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11 };

int  ia[3][4] = { {0}, {4}, {8} };            //初始化了每行第一个元素,其余的默认初始化

int  ia[3][4] = { 0, 1, 2 };                    //初始化了前3个元素,其余默认初始化

4.4.2 多维数组的下标引用

4.4.3 指针和多维数组

         int  (*ip)[4];             //ip是一个指向含有4个int元素的数组指针。

         int  *ip[4];                //ip是一个含有4个元素的指针数组。

4.4.4 用typedef简化指向多维数组的指针

         typedef  int int_array[4];             //int_array是int[4]的别名

         int_array  *p = ia;           //ia是一个3*4二维数组

         for(int_array  *p = ia; p != ia + 3; ++p)           //循环行

                   for(int  *q = *p; q != p + 4; ++q)             //循环每行的每列

                            cout<< *q <<endl;

抱歉!评论已关闭.