C++Primer学习笔记(三)数组和指针

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

数组和容器的相同点和不同点
相同点: 都是存储单一数据类型对象的容器
不同点:
1)数组的长度是固定的,而且程序员无法知道一个给定数组的长度(没有获得其容量大小的size操作),也不提供push_back操作
2)如果要更改数组的长度,只能创建一个更大的数组,然后将原数组中的元素copy过去.

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

一些编译器允许将数组赋值作为编译器扩展。但是如果希望编写的程序能在不同的编译器上运行,则应该避免使用像数组赋值这类依赖于编译器的非标准功能。

建议:尽量避免使用指针和数组
指针和数组容易产生不可预料的错误。其中一部分是概念上的问题:指针用于低级操作,容易产生与繁琐细节相关的(bookkeeping)错误。其他错误则源于使用指针的语法规则,特别是声明指针的语法。
许多有用的程序都可不使用数组或指针实现,现代C++程序采用vector类型和迭代器取代一般的数组、采用string 类型取代C 风格字符串。

指针和迭代器的异同点
相同点:指针和迭代器一样,提供对所指对象的间接访问
不同点:指针指向单个对象,而迭代器只能用于访问容器内的元素.

指针可能的取值
保存一个对象的地址;指向某个对象后面的另一对象;0值(表明不指向任何对象)

 

避免使用未初始化的指针

对大多数的编译器来说,如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中存放的位内容。使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据。因此,在对未初始化的指针进行解引用时,通常会导致程序崩溃。

C++ 语言无法检测指针是否未被初始化,也无法区分有效地址和由指针分配到的存储空间中存放的二进制位形成的地址。建议程序员在使用之前初始化所有的变量,尤其是指针。

指针初始化和赋值操作的约束
对指针初始化或赋值只能使用以下四种类型的值:
1)0值常量表达式: 字面值常量0或可获得0值的整型const对象或字面值常量 0
2)类型匹配的对象的地址
3)另一对象之后的下一地址
4)同类型的另一个有效指针

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* 指针操纵它所指向的对象。

指针和引用的比较
1)引用总是指向某个对象,定义引用时没有初始化是错误的
2)给引用赋值修改的是该引用关联的对象的值,而并不是使引用与另一个对象关联

 

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

 

C++ 允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。而计算数组超出末端位置之后或数组首地址之前的地址都是不合法的。

 

如果指针指向const对象,则不允许用指针来改变其所指的const值,为了保证这个特性,c++要求指向const对象的指针页必须是const的.

不能使用指向 const 对象的指针修改基础对象,然而如果该指针指向的是一个非 const 对象,可用其他方法修改其所指的对
象。

如果把指向 const 的指针理解为“自以为指向 const 的指针”,这可能会对理解有所帮助。

 

操纵 C 风格字符串的标准库函数
strlen(s) 返回 s     的长度,不包括字符串结束符 null
strcmp(s1, s2)       比较两个字符串 s1 和 s2 是否相同。若 s1 与 s2 相等,返
                   回 0;若 s1 大于 s2,返回正数;若 s1 小于 s2,则返回负数
strcat(s1, s2)       将字符串 s2 连接到 s1 后,并返回 s1
strcpy(s1, s2)      将 s2 复制给 s1,并返回 s1
strncat(s1,s2,n)      将 s2 的前 n 个字符连接到 s1 后面,并返回 s1
strncpy(s1,s2, n)    将 s2 的前 n 个字符复制给 s1,并返回 s1

传递给这些标准库函数例程的指针必须具有非零值,并且指向以 null 结束的字符数组中的第一个元素。其中一些标准库函数会修改传递给它的字符串,这些函数将假定它们所修改的字符串具有足够大的空间接收本函数新生成的字符,程序员必须确保目标字符串必须足够大。

 

动态数组的定义

动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数实现初始化;如果数组元素是内置类型,则无初始化

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

动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。

delete [] pia;
该语句回收了 pia 所指向的数组,把相应的内存返还给自由存储区。在关键字 delete 和指针之间的空方括号对是必不可少的:它告诉编译器该指针指向的是自由存储区中的数组,而并非单个对象。
如果遗漏了空方括号对,这是一个编译器无法发现的错误,将导致程序在运行时出错。

 

多维数组
严格地说,C++ 中没有多维数组,通常所指的多维数组其实就是数组的数组

posted @ 2020-07-26 16:17  thsj  阅读(121)  评论(0)    收藏  举报