(二)数组与指针

  数组

    数组也是存储单一数据类型对象的容器,与vector相比,它的长度固定,也没有获取长度的操作。现代C++多用vector代替数组。

数组的维数必须用常量表达式定义。非const变量以及运行阶段才能知道值的const变量都不能定义数组的维数。例如:

    const unsigned buf_size=512,max_files=50;

    int staff_size=27;

    const unsigned sz=get_size();    //直到运行get_size()才能获得sz的值

    char buffer[buf_size];          //OK

    string fileTable[max_file+1];    //OK

    double salaries[staff_size];      //NO

    int score[get_size()];           //NO,非常量表达式

    int val[sz];                   //NOsz直到运行时才能知道

初始化时可以进行显示初始化,例如:

    const unsigned sz=3;

    int ia[]={0,1,2,4};             //默认长度为4

    int ib[sz]={1,2,3};

    sting ic[sz]={hi,boy};      //默认为{hi,boy,””}

如果没有显示初始化,那么数组元素会像普通变量一样初始化:在函数体外,元素默认为0;在函数体内,无初始化;如果元素类型为类类型,无论数组在什么位置,元素都会自动调用默认构造函数进行初始化,如果类没有默认构造函数,那么就必须显示初始化。

字符数组即可以用用花括号进行初始化,还可以用字符串字面值。

    char ca1[]={C,+,+};        //长度为3

    char ca2[]={C,+,+,\0};     //长度为4

    char ca3[]=C++;             //长度为4

    char ca4[3]=C++;            //错误

vector不同,数组不能用别的数组初始化,也不能将一个数组赋值给另外一个数组。

    int ia[]={1,2,3};

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

    ib=ia;                //错误

用下标访问元素时,vector使用vector<>::size_type作为下标的类型,而数组的下标类型为size_t

    int main(){

    const size_t array_size=10;

    int arr[array_size];

    for(size_t ix=0;ix!=array_size;++ix)

    return 0;

}

 

指针

vector的遍历可以用下标或者迭代器实现,而数组的遍历可以用下标或者指针实现。指针用于指向对象,与迭代器一样,提供对所致的对象的间接访问,只是指针结构更通用一些。

与迭代器不同的是,指针指向单个对象,迭代器只能访问容器里面的元素。

指针保存的是对象的地址。每个指针都有一个与之关联的数据类型,这个类型决定了指针所指向对象的类型。

    vector<int> *vec;  //vec指向了一个vector<int>类型的数据。

    int* ip;          //ip指向了一个int型数据

    string* sp;       //sp指向了一个string类型的数据。

    double da,*db;    //dadouble型,dbdouble型指针

一个有效的指针必然是以下三个状态之一:保存一个特定对象的地址、指向某个对象后面的另一个对象、0值。没有初始化的指针是无效的,直到指针赋值以后才能够使用它。

    int ival=1204;

    int *pi=0;        //pi赋给了0值,等价于pi=NULL,有效。

    int *p1=&ival;    //有效

    int *p2;         //没有初始化

    int *p3=p1;      //有效

    p3=0;          //现在p3不指向任何对象

避免使用没有初始化的指针。指针的赋值情况有:0值常量(0或者0值的const量)、类型匹配的对象的地址、另一对象之后的下一个地址、同类型的另一个有效地址。

void*指针可以保存任何对象的地址。void*表明该指针与一地址值相关,但不清楚存储在该地址上的对象的类型。void*指针只支持几种有限的操作:与另一指针进行比较、像函数传递void*指针、函数返回void*指针、给另一个void*指针赋值。不允许使用void*指针操纵它所指向的对象。

可以使用指针访问数组元素。

    int ia[]={0,2,4,6,8};

    int *ip=ia;

    ip=&ia[4];

    int *ip2=ia+2;

    std::ptrdiff_t=ip2-ip;    //两个指针之间的距离:-2

使用下标访问数组时,只要下标操作指向的是数组元素就可以进行。

    int ia[]={0,2,4,6,8};

    int *p=&ia[2];

    int j=p[1];    //ok,p[1]等价于*(p+1), *(ia+3),ia[3] 

    int k=p[-2];   //ok,p[-2]即为ia[0]

指针是数组的迭代器。以下pend指针超出了数组的范围,它不能进行引用运算,但是可以进行比较。这个循环非常类似于vector中的迭代器的操作:for(vector<int>::iterator iter=vec.begin;iter!=vec.end();iter++)

    const size_t sz=5;

    int arr[sz]={0,2,4,6,8};

    for(int *pbegin=arr,*pend=arr+sz;pbegin!=pend;pbegin++)

        cout<<*pbegin<<endl;

const与指针限定符有两种形式:const double *ptrdouble * const ptr

第一种的含义是不可用指针修改对象的内容。但是可以改变指针本身的内容,即指针可以赋给一个新的地址。如果所指对象不是const类型,那么不能通过指针修改这个对象,但可以用其它途径修改。如果指针所指内容是const,那么这个指针必须声明成第一种形式,否则不能通过编译。

    const double pi=3.14;

    double *ptr=π  //error,ptr is a plain pointer

    const double *ptr=π  //ok,a pointer to const

    const void *pv=π    //ok

    void * ppv=π    //error

    double dal=3.14;

    const *dptr=&dal;    //ok,but cannot change dal through dptr

    *dptr=3;     //error

    dptr=ptr;    //ok

第二种是const指针,它的值不能改变,即不能再使它指向其它对象。

    int a=5;    

    int * const pa=&a;

    pa=pa;    //error

    *pa=6;    //ok

还可以定义不可用指针修改所指内容的const对象。

        int a=9;

    const intconst pa=&a;

typedef中使用指针往往带来意外的结果。例如:

    typedef string *pstring; //将指向string的指针定义为pstring

    const pstring cstr;

那么这里定义的cstr是什么类型?非常容易误解为const string* cstr,事实上,const修饰的是cstr,

这个定义等价于string * const cstr。与string const s等价于const string s一样,这个语句也定价于pstring constcstr。

 

c风格字符串

以下中,cst1cst2外,其余都是c风格字符串。

    char cst1[]={C,+,+};

    char *cst2=cst1;

    char cst3[]={C,+,+,\0};

    char *cst4=cst3;

    char *cst5=C++;

    char cst6[]=C++;

    const char *cst7=C++;

事实上,c风格字符串的类型为const char *类型。

       char *cp="C++";

       cp[1]='-';   //错误,内容是不可改变的

  char cal[]={'C','+','+'};

  cp=cal;

  cp[1]='-';   //正确

   C++中用const char*类型来处理C风格字符串。可以用如下方式遍历字符串:

       const char *cp=what a great day!;

       while(*cp)

           {do sth whith *cp; cp++;}

处理C风格字符串的头文件为<cstring>。传入库中函数的指针必须非空且指向以null结束的字符串中。char ca[]={C,+,+};调用strlen(ca)将发生错误。

库函数有:strlen(s)——s的长度,不包含null;strcmp(s1,s2)——比较s1与s2的大小,s1大于s2时返回正数;strcat(s1,s2)——s2链接到s1并返回s1;strcpy(s1,s2)——s2复制到s1并且返回s1;strncat(s1,s2,n)——s2的前n个连接到s1并返回s1,注意要包含null;strncpy(s1,s2,n)——s2的前n个复制到s1并且返回s1。当然C++中提供简单的字符串比较方式:p1>p2。

尽管C++支持C风格字符串,但是应避免使用这种类型。

 

创建动态数组

虽然数组的长度是固定的,但是动态分配的数组不必在编译时知道其长度,可以在运行时才确定其长度。用于动态分配的对象的存储空间称为自由存储区或者堆。

    int *pia=new int[10]; //new分配了含有10int的数组,并将第一个指针返回

    delete[] pia; //将分配的内存返回给自由存储区

动态分配数组时,如果是类类型,数组元素调用默认构造函数,如果是内置类型,不初始化,但可以添加括号进行默认初始化,int *p=new int[10]()

     动态数组是允许运行时才确定长度的。

         size_t n=getSize(); //即使n=0,任然可以运行,new返回一个非0指针

         int *p=new int[n];

         for(int* q=p;q!=p+n;q++)

    

多维数组

    C++中没有多维数组,多维数组指的是数组的数组。int ia[3][4]={{1,2,3,4},{5,6,7,8},{9}}

多维数组名是一个指针,它指向了一个内层数组。int (*ip)[4]ip是一个指针,指向了int[4]

        int ia[3][4];

        typedef int arr[4];//arrint[4]类型,即arrint *const

        arr *p=ia;       //等价于int (*p)[4]=ia;p指向的数据是int *const

                  //那么p[0]不能被赋值,而p可以

        arr m=&ia[2];    //error,等价于int m[4]=ia[2];m被赋值不能

        p[0]=ia[0];      //errorpia是指向同一对象,但是p[0]不能赋值

你可能感兴趣的:(C++Primer 数组与指针)