C++Primer读书笔记----数组和指针(1)

数组和指针

C++ 语言提供了两种类似于 vector 和迭代器类型的低级复合类型——数组和指针。
它们的区别:数组的长度是固定的。数组一经创建,就不允许添加新的元素。指针则可以像迭代器一样用于遍历和检查数组中的元素。

数组的显著缺陷在于:数组的长度是固定的,而且程序员无法知道一个给定数组的长度。数组没有获取其容量大小的 size 操作,也不提供 push_back 操作在其中自动添加元素。如果需要更改数组的长度,程序员只能创建一个更大的新数组,然后把原数组的所有元素复制到新数组空间中去。

4.1数组

数组是由类型名、标识符和维数组长的复合数据类型,类型名规定了存放在数组中的元素的类型,而维数则指定数组中包含的元素的个数。

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

数组的定义和初始化

非const变量以及要到运行阶段才知道其值的const变量都不能用于定义数组的维数。数组的维数必须在一对方括号[]内指定。

const unsigned buf_size = 512;
int arr1[buf_size + 1 ];
const int buf_size1 = GetSize();
int arr2[buf_size1]; //IntelliSense: expression must have a constant value
int buf_size2 = 27;
int arr3[buf_size2]; //IntelliSense: expression must have a constant value

显式初始化数组元素

在定义数组时,可为其元素提供一组用逗号分隔的初值,这些初值用花括号{}括起来,称为初始化列表。

const unsigned buf_size = 3;
int arr1[buf_size + 1 ] = {1,2,3,4};

如果没有显式提供元素初值,则数组元素会像普通变量一样初始化:

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

2)在函数体内定义的内置数组,其元素无初始化;

3)不管数组在哪里定义,如果其元素为类类型,则自动为该数组的元素提供显式初始化。

显式初始化的数组不需要指定数组的维数值,编译器会根据列出的元素来确定数组的长度。int arr[] = {1, 2, 3};

如果指定了数组维数,那么初始化列表提供的元素个数不能超过维数值。如果维数值大于列出的元素初值个数,则只初始化前面的数组元素;剩下的其他元素,若是内置类型则初始化为0,如果是类类型则调用该类的默认构造函数进行初始化。

特殊的字符数组

字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。字符串字面值包含一个额外的空字符(null)用于结束字符串。当使用字符串字面值来初始化创建的新数组时,将在新数组中加入空字符。

char arr[] = "str";

 

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

与vector不同,一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另一个数组,这些操作都是非法的。

与vector类型不同,数组不提供push_back或者其他的操作在数组中添加新元素,数组一经定义,就不允许再添加新元素。

如果必须在数组中添加新元素,程序员就必须自己管理内存:要求系统重新分配一个新的内存空间用于存放更大的数组,然后把原数组的所有元素复制到新分配的内存空间中。

数组操作

数组下标的正确类型则是size_t

导致安全问题的最常见原因是所谓"缓冲区溢出(buffer overflow)"错误。当我们在编程时没有检查下标,并且引用了越出数组或者其他类似数据结构边界的元素时就会导致这类错误。

4.2 指针的引入

vector的遍历可使用下标或迭代器实现,同理,也可用下标或指针(pointer)来遍历数组。指针是指向某种类型对象的复合数据类型,是用于数组的迭代器:指向数组中的一个元素。在指向数组元素的指针上使用解引用操作符*(dereference operator)和自增操作符++(increment operator),与在迭代器上的用法类似。

指针的概念很简单:指针用于指向对象。与迭代器一样,指针提供对其所指对象的间接访问,只是指针结构更通用一些。与迭代器不同的是,指针用于指向单个对象,而迭代器只能用于访问容器内的元素。指针保存的是另一个对象的地址。

int i = 100;
int *j = &i;
int &k = i;

cout << j << endl; //0012FF60
cout << k << endl; //100

&i中的&符号是取地址操作符,当此操作符用于一个对象上时,返回的是该对象的存储地址。取地址操作符只能用于左值。

尽量避免使用指针和数组

许多有用的程序都可不使用数组或指针实现,现代C++程序采用vector类型和迭代器取代一般的数组、采用string类型取代C风格字符串。

4.2.2 指针的定义和初始化

每个指针都有一个与之关联的数据类型,该数据类型决定了指针所指向的对象的类型。

1. 指针变量的定义

C++语言使用*符号把一个标识符声明为指针。

int *j;
int* k;

另一种声明指针的风格

在定义指针变量时,可用空格将符号*与其后的标识符分隔开来。

int* k, l; //k is a point, l is not a point...

指针的可能取值

一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一个对象;或者是0值。若指针保存0值,表明它不指向任何对象。未初始化的指针是无效的,知道给该指针赋值后,才可使用它。

 避免使用未初始化的指针

很多运行时的错误都源于使用了未初始化的指针。

就像使用其他没有初始化的变量一样,使用未初始化的指针时的行为C++标准中并没有定义使用未初始化的指针,它几乎总会导致运行时崩溃。

如果可能的话,除非所指向的对象已经存在,否则不要先定义指针,这样可避免定义一个未初始化的指针。

如果必须分开定义指针和其所指向的对象,则将指针初始化为0.因为编译器可检测出0值的指针,程序可判断该指针并未指向一个对象。

指针初始化和赋值操作的约束

(1)0值常量表达式

(2)类型匹配的对象的地址

(3)另一个对象之后的下一个地址

(4)同类型的另一个有效指针

把int型变量赋给指针是非法的,尽管int型变量的值可能为0,但允许把数组0或在编译时可获得0值的const量赋给指针。

还可以使用C++语言中继承下来的预处理器变量NULL,该变量在cstdlib头文件中定义,其值为0.如果在代码中使用了这个预处理器变量,则编译时自动被数值0替换。因此,把指针初始化为NULL等效于初始化为0值。

void*指针

C++提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址。

void*指针只支持几种有限的操作:与另一个指针进行比较;向函数传递void*指针或从函数返回void*指针;给另一个void*指针赋值。不允许使用void*指针操纵它所指向的对象。

(未完待续。。。)

 

posted @ 2012-12-16 19:59  Sam.Sun  阅读(411)  评论(0编辑  收藏  举报