c++ Primer 笔记(第四章)

    技术2022-07-10  114

    数组和指针

    类似于vector和迭代器类型的低级复合类型。区别在于,数组的长度是固定的。数组一经创建,就不允许添加新的元素。指针则可以像迭代器一样用于遍历和检查数组中的元素。

    现代C++程序应尽量使用vector和迭代器类型,而避免使用低级的数组和指针。设计良好的程序只有在强调速度时才在类实现的内部使用数组和指针。

    数组

    数组的定义和初始化

    数组定义中的数据类型可以是内置数据类型或类型类型;除引用之外,数组元素的类型还可以是任意的复合类型。没有所有元素都是引用的数组。

    数组的维数必须用值大于等于1的常量表达式定义;此常量表达式只能包括整型字面值常量、枚举常量或者用常量表达式初始化的整型const对象,非const变量以及要在运行阶段才知道其值的const变量不行。

    显示初始化

    在函数体外定义的内置数组,其元素均初始化为0;

    除非显式提供元素初值,否则内置数据类型的局部数组没有初始化,除了给元素赋值外,其他使用这些元素的操作没有意义;

    int ia[] = {0,1,2}; const unsigned arr_size = 5; int ia[arr_size] = {0,1,2}; //相当于 ia = {0,1,2,0,0} string str_arr[arr_size] = {"hi", "bye"} //相当于str_arr = {"hi", "bye", "", "", ""}

    字符数组

    char ch1[] = {'c','+','1'}; //字符字面量初始化 char ch2[] = "c+1"; //字符串字面值初始化,其包含一个额外的空字符null作为结尾 const char ch3[6] = "Daniel"; //error:字符串含7个元素

    不允许数组直接复制和赋值

    数组操作:注意检查数组下标值,防止数组越界

    指针

    指针的定义和初始化

    一个有效的指针必然是以下三种状态之一:

    保存一个特定对象的地址

    指向某个对象后面的另一个对象

    保存0值,表明它不指向任何的对象

    int ival = 1024; //下列定义和赋值都是合法的 int *pi = 0; //保存为0值 int *pi2 = & ival; //初始化为ival的地址 int *pi3; //ok,but dangerous,未初始化 pi = pi2; //pi和pi2都指向ival pi2 = 0; //将pi2改为不指向任何对象 //应避免使用未初始化的指针,会导致程序崩溃

    tips尽量不要先定义指针,再初始化。如果必须分开定义指针和其所指向的对象,则将指针初始化为0

    对指针进行初始化或赋值只能使用一下四种类型的值:

    0值常量表达式

    类型匹配的对象的地址

    另一个对象之后的下一地址

    同类型的另一个有效地址

    int ival; int zero = 0; const int c_val = 0; int *pi = ival; //error,int型变量赋给指针是非法的 pi = zero; //error,如上,即使int变量的值为0 pi = c_ival; //ok,编译时获得0值得const变量可以赋给指针 pi = 0; //ok,字面常量0 -------------------- //cstdlib头文件中定义 #define NULL 0 int *pi = NULL; //ok -------------------- //void*指针可以保存任何类型对象的地址,表明该指针与一地址值有关,但不清楚存储在改地址上的对象的类型 double obj = 3.14; double *pd = &obj; void *pv = &obj; pv = pd;

    指针的操作

    生成左值得的解引用操作

    利用这个功能可修改指针所指对象的值;也可修改指针本身的值

    string s("hello word"); string *sp = &s; *sp = "goodbye"; //修改s对象的值 string s2 = "some value"; sp = &s2; //修改sp指针的值,使其指向说s2-不需要对指针进行解引用

    tips 注意给指针赋值和通过指针进行赋值的区别

    如果对左操作数进行解引用。则修改的是指针所指对象的值;如果没有解引用操作,则修改的是指针本身的值

    指针和引用的区别

    第一,定义引用没有初始化是错误的;

    第二,给引用赋值修改的是该引用所关联对象的值,而不是使引用与另一个对象关联,引用一经初始化,就始终指向同一个特定对象(这就是为什么引用必须在定义时初始化的原因)

    指向指针的指针

    将指针存储的地址存放在另一个指针中

    int ival = 1024; int *pi = &ival; //pi points to an int int **ppi = π //ppi points to a pointer to int int *pi2 = *ppi; //pi2 points to pointer pi

    使用指针访问数组元素

    指针的算数操作

    与其使用下标操作,倒不如通过指针的算数操作来获取指定内容的存储地址。指针的算数操作和迭代器的算术操作以相同的方式实现(也具有相同的约束);

    指针的算数操作只有在原指针和计算出来的新指针指向同一个数组的元素,或指向该数组存储空间的下一单元时才是合法的。如果指针指向一对象,我们还可以在指针上加1从而获取指向相邻的下一对象的指针。

    解引用和指针算数操作之间的相互作用

    注意操作符的优先级

    指针和const

    指向const对象的指针

    定义时不需要对它进行初始化;

    不允许使用指针来改变其所指的const值;

    不能把一个const对象的地址赋给一个普通的、非const对象的指针;

    不能使用viod* 指针保存const对象的地址,而必须使用const void*类型的指针;

    允许把非const对象的地址赋给指向const对象的指针;

    const int *ptr; *ptr = 66; //error const double pi = 3.14; double *ptr1 = π //error const double *cptr = π //ok const int a = 66; const void *ptr2 = &a; //ok void *p = &a; //error double dval = 3.14; ptr = &dval; //ok

    并不能保证指向const的指针所指对象的值一定不可修改,只是不能通过该指针来修改而已。在实际程序中,指向const的指针常用来作函数的形参,防止传递给函数的实际对象在函数中不因为形参而被修改

    const指针

    指针本身不能被修改,即不能使该指针指向其他对象;

    必须在定义时初始化;

    int a = 0; int *const p = &a; //读作:p是指向int型对象a的const指针 从右往左读

    指向const对象的const指针

    既不能修改指针所指对象的值,也不能修改指针的指向

    const double pi = 3.14159; const double *const ptr = π //ptr首先是一个const指针,指向double类型的const对象

    把声明语句重写置const于类型之后更便于理解

    typedef string *pstring; const pstring cstr; //上面两句等价于 string *const cstr;

    动态数组

    虽然数组长度是固定的,但动态分配的数组不必在编译时知道其长度,可以在运行时才确定数组长度。于数组变量不同,动态分配的数组将一直存在,直到程序显式释放它为止。

    C语言使用标准库函数malloc和free在自由存储区中分配内存,而C++语言则使用new和delete表达式实现相同功能。

    int *pia = new int[10]; //分配了一个含有10个int型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针pia //自由存储区中创建的数组对象是没有名字的,只能通过其地址间接访问堆中的对象
    Processed: 0.015, SQL: 9