《c++ primer》第四章--数组和指针

来源:互联网 发布:库班和诺维斯基 知乎 编辑:程序博客网 时间:2024/06/05 10:38

数组是c++中类似于标准库vector类型的内置数据结构,与vector相似,数组也是一种存储单一类型对象的容器,其中每个对象没有单独的名字,而是通过它在数组中的位置对它进行访问。

与vector类型相比,数组的显著缺陷在于:数组的长度是固定的,而且程序员无法知道一个指定数组的长度。数组没有获得其容量大小的size操作,也不提供push_back操作在其中自动添加元素。如果需要更改数组的长度,程序员只能创建一个更大的新数组,然后把原数组的所有元素复制到新数组空间中去。~~~~(>_<)~~~~太费劲了...

在现在c++中,更多的使用vector容器来取代数组,数组被严格限制与程序内部使用,只有当性能测试表明使用vector无法达到必要的速度要求时,才使用数组。(看来vector容器虽然用起来很方便,但是速度上不太行啊~~)。

&4.1.1

关于数组的定义和初始化:

数组的维数必须用大于等于一的常量表达式定义,此常量表达式只能包含整形字面值常量、枚举常量或者用常量表达式初始化的整型const对象。非const变量以及要到运行阶段才知道其值的const变量都不能用于定义数组的维数。  ————注意,是数组的维数,不是数组中某个 元素的值~~

例子:

//  both buf_size and max_files are const

const unsigned buf_size = 512,max_files = 20 ;

int staff_size = 27;   nonconst

const unsigned sz = get_size() ; //const value not known until run time

char input_buffer [ buf _ size ] ; //ok:const variable

string fileTable  [max_size +1 ]; // OK:const expression

double salaries [staff_size]; //error: non const variable

int test_scores  [ get_size ]; //error: non  const expression

int vals [ sz ]; //error: size not known until run time

首先,虽然staff_size是用的字面值常量进行初始化,但是staff_size本身是一个——————非const对象,所以只有在运行时才能获得它的值————————。

因此,使用该变量来定义数组维数是非法的。而对于sz,尽管它是一个const对象,但是它的值要等到运行时调用get_size函数后才知道,因此,它也不能用于定义数组维数。另一方面,由于 max_files 是const 变量,因此表达式

max_size+1

是常量表达式,编译时即可计算出该表达式的值为21。


1.

显示初始化数组元素:

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

const unsigned array_size=3;

int ia  [ array _ size ] = { 0 , 1,  2 } ;

ps:

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

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

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

※不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显示初始化。


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

显式初始化的数组不需要提供指定数组的维数,编译器会根据列出的元素个数类确定数组的长度。

EG:

int  a [ ] = { 0 , 1 , 2 } ;

如果初始化列表中维数小于列出的元素个数,那么会怎么样呢?

EG:

int ia  [ 3 ] = { 0 , 1 , 2 , 3 , 4 , };     //error:   当家应该能猜到这个错误是什么。。。初始值设定太多(⊙﹏⊙)b


※ 2

特殊的字符数组:

字符数组既可以用一组由花括号括起来、用逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。然而,要注意的是两种初始化方式并不完全是相同,字符串字面值包含一个额外的空字符(NULL) 用于结束字符串。当使用字符串字面值来创建新数组时,将在新数组中加入空字符。

char  ca [  ] = { " C++" } ;

ca有四个元素,c,+,+,‘\0’   

※ 注意:

与vector不同的是,一个数组不能用另一个数组进行初始化,也不能将一个数组赋值给另一个数组,这些操作都是违法的~~!违法!



※4.1.2

数组的操作:

和vector操作一样,数组元素可以用下表操作符来访问,数组元素也是从0开始计数。

在这里要注意一点,在c++中,vector使用vector::size_type作为下表类型,而数组下标的正确类型则是size_t(也就是说,用int)也是可以的)~~~

i例如:

int main()
{
const size_t a_size = 5;
const int a_s = 6;
int ia[a_size];
int iaa[a_s];
}

这两种方式都是可以的,但是在编程序时千万别忘了int a_s前面的const,如果忘了就去上面看一下。


ps:

在我们使用数组的时候,千万要对数组下标进行检查,以防 “ 过界 ”,导致安全问题的最常见原因是所谓 “ 缓冲区溢出 ”,就是因为在编程时没有检查下标,这种错误常常会让人忽略,但确实是不容轻视的一个错误。


※4.2

指针的引入:

首先,指针的概念很简单:用于指向对象。和迭代器一样,指针提供对其所指对象的间接访问,只不过是指针结构更通用一些。当然,和迭代器不同的是,指针指向单个对象,而迭代器只能用于访问容器内的元素。

*****************具体来说,指针保存另一个对象的地址~


※4.2.2

指针的定义:

c++中使用*符号把一个标识符声明为指针:
vector  < int >   *pvec;

int  *p1;

string *ps;

在c++中  也可以使用这种风格声明指针:
string*     ps;

ps也是指向string对象的一个指针。

PS:

不过在,上面的第二种声明指针的风格很容易让人误解,而且在下面这个语句中:

string*    ps1,ps2;

只有ps1是一个string * 类型,而ps2只是一个普通的string类型的对象~~!


4

指针可能的取值:
一个有效指针必然是一下三种状态之一:

( 1 ): 保存一个特定对象的地址;

( 2 ): 指向某个对象后面的另一个对象;

( 3 )   : 0值。


5:

我们应该尽量避免使用未初始化的指针,虽然指针可以不声明时马上定义,但是就像使用其他没有初始化的变量一样,在使用它的时候几乎总会导致运行时崩溃。然而,导致崩溃的这一原因很难发现。

对大多数编译器来说,如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中存放的位内容。 

 

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


对指针进行以上操作只能使用一下四种类型的值:

1: 0值常量表达式

2: 类型匹配的对象的地址。

3: 另一个对象之后的下一个地址。

4: 同类型领一个有效指针。



7

特殊类型的指针:void*指针

它可以保存任何类型对象的地址~

当然,不允许使用void*类型的指针操纵它所指向的对象。



※4.2.3

指针操作

指针提供间接操纵其所指对象的功能,与对迭代器进行解引用一样,对指针进行解引用可以访问它所指的对象,* 操作符将获取指针所指的对象。

string s ( " hello  world " ) ;

string * SP = & s ;

cout << * sp; //   prints hello  world;



如果给你一个指针,要将指针所指的对象进行修改,可以怎样做呢?


* SP =  “ good bye ” ;

这样指针所指的对象就改成了 “ good bye ” ,也没啥难度是吧。。。




※  注意给指针赋值或通过指针进行赋值:

对于初学的人来说,如何区分是给指针赋值还是通过指针进行赋值确实是挺困难的,不过我们可以记住一个重要的方法: 如果对左操作符进行了解引,那么就是修改指针所指对象的值,如果没有使用解引用,那么就是修改指针本身。

考虑下面这个例子:


string  s1 (  " some value " ) ;

string *sp1=&s1;


string s2 ( " another " ) ;

string *sp2=&s2;


*sp1="  a new  value " ;

//  这里修改了s1的值。

sp1= sp2;

//  这里修改了sp1的指针,使其和sp2指向相同的对象。



※在这里顺便进行一下指针和引用的比较


虽然两者都可以间接访问另一个值,但是它们有两个重要的区别:

1: 定义引用时没有初始化是错误的;

2: 给引用赋值修改的是该引用所关联的对象,因为它是对象的别名,。



4.3  c风格字符串

2.2节中我们已经用过了字符串字面值,并了解了字符串的类型就是字符串常量的数组,现在可以更明确地认识到:它是一个 const char类型的数组,c++从c语言继承下来的一种通用结构是  c风格字符串,而字符串字面值就是这种类型的实例,然而,c风格字符串既不能确切的归结为c的类型,也不能归结为c++,它是以空字符NULL结束的字符数组。

0 0