第4章 数组和指针

4.1、数组

    数组是由类型名、标识符和维数组成的复杂数据类型。

4.1.1、数组的定义和初始化

    数组的维数必须用大于等于1的常量表达式定义。该表达式只能包含整型字面值常量、枚举常量或者用常量表达式初始化的整型const对象(即必须编译时就可以知道的值,对于函数返回的值赋给const则不可以,因为是运行才知道。int a=1;int b[a];也是不可以)。

    显示初始化数组元素:如int a[] = {0,1,2};中括号里元素对于显示初始化可有可无,如果是a[5]= {0,1,2};则后面两个元素初始化为0。没有显示提供元素初值,则初始化方式:(1)在函数体外定义的内置数组,其元素初始化为0(string则补空字符串);(2)在函数体内定义的内置数组,其元素无初始化(这边如果在函数内定义a[5]={0,1,2};则a[3]和a[4]还是会初始化为0的!纯粹的a[5]则不会初始化,输出a[i]会产生运行错误。main函数内也一样);(3)不管数组在哪定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显示初始化。

    特殊的字符数组字符串字面值包含一个额外的空字符用于结束字符串。如char ca[]={‘c’,’+’,’\0’};而char ca[3]=“C++”则是错误,需要四个元素,最后一个为’\0’。

       数组不能用另一个数组初始化,也不能将一个数组赋值给另一个数组。

4.1.2、数组操作

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

4.4、指针的引入

4.2.1、什么是指针

    如string s(“hello world”);string *sp = &s;*sp的*操作符表明sp是一个指针变量。&s的&符号是取地址操作符,当此操作符用于一个对象上时,返回的是该对象的存储地址。取操作符只能用于左值

4.2.2、指针的定义和初始化

       指针还可以string* sp;这么定义。注意string* sp1,sp2;中sp2并非指针,只是普通string对象而已。在对未初始化的指针进行解引用会导致程序崩溃。

       对指针进行初始化和赋值只能用以下四种类型的值:(1)0值常量表达式(必须编译时就可知到值);(2)类型匹配的对象的地址;(3)另一对象之后的下一地址;(4)同类型的另一个有效指针。还可以用C语言继承下来的预处理器变量NULL,该变量在cstdlib头文件中定义,其值为0。(注:预处理器变量不是在std命名空间中定义的,因此不需要std::NULL)。

       指针初始化或赋值必须保证类型匹配。

       void*指针:该指针可以保存任何类型的对象地址,支持操作有:(1)与另一个指针进行比较(地址间的比较);(2)向函数传递void*指针或从函数返回void*指针;(3)给另一个void*指针赋值。注:不允许使用void*指针操纵它所指向的对对象

4.2.3、指针操作

    指针和引用(reference,如int &r1 = ival;&在右边是取地址)的比较:两个区别(1)引用总是指向某个对象(始终指向),定义引用时没有初始化是错误的(即引用是对象的一个别名)。(2)赋值行为差异,给引用赋值修改的是该引用所关联的对象的值,而不是使引用与另一个对象关联(因为引用是对象的别名,对引用赋值相当于改变对象内容,而这个引用还是该对象的别名)。给指针直接赋值则是改变指针指向。

    指向指针的指针(**),也很简单,常识性知识。(再提一下,*操作符这边称“解引用操作符”)。

4.2.4、使用指针访问数组元素

    两个指针减法操作的结果是标准库类型ptrdiff_t的数据。它也是一种与机器相关的类型,在cstddef头文件中定义(与size_t一样)。这个是signed整型。指针具有0值(空指针),加减0仍然是合法的。

    下标和指针:

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

       int *p =&ia[2];

       cout<<p[1]<<endl;//访问的是ia[3]

       cout<<p[-2]<<endl;//相当于访问ia[0];这边访问p[-10],p[10]都不会产生运行异常,会返回随意数。因此没有java安全。

    对于p1 = p+3;p1指向超出末端的位置,只能用来与其他指针比较,或者用做指针算术操作表达式的操作数,对其解引用将得到无效值

4.2.5、指针和const限定符

    指向const对象的指针:C++强制要求指向const对象的指针也必须具有const特性。const double *cptr;注意这边const限定了cptr指针所指向的对象类型,而并非cptr本身,即cptr本身不是const,因此在定义时不需要对它进行初始化。*cptr = 42;是错误的,不能通过cptr修改其所指对象的值。

    把一个const对象的地址赋值给一个普通的、非const对象的指针也会导致编译时的错误。不能使用void*指针保存const对象的地址,而必须使用constvoid*类型。允许把非const对象的地址赋值给指向const对象的指针。这将导致可用其他方法修改const对象的指针(它只是个自以为指向const的指针)。用非const指针指向该内容,再修改该指针,那么const对象内容也就变了。

    const指针:本身的值不能修改,如int *const curErr = &errNumb;这时该指针必须在定义时初始化。但指针指向的内容完全取决于该对象类型,而并非也是const。

    指向const对象的const指针:constdouble pi = 3.14;const double *const pi_ptr = &pi;两步都不能少。

    指针和typedef:

typedef string *pstring;

const pstring cstr;

问cstr是什么类型?其实挺好理解的,pstring是指向字符串的指针,const修饰pstring,所以是指向字符串的const指针,所以定义等价为:string *const cstr;

    注意:string s;typedef string *pstring;

const pstring cstr1 = &s;

pstring const cstr2 = &s;

string *const cstr3 = &s;三者等价,因为const既可以放在类型前也可以是后

4.3、C风格字符串

    以空格符null(’\0’)结束的字符数组。

    strlen(s);返回s的长度,不包括字符串结束符null。当初始化

char ib[] = {'c','+','+'};strlen(ib);是没有意义的,必须显示在最后添加‘\0’。*ib为c。cout<<ib也会导致c++后面跟上很多乱码。

    strcmp(s1,s2);比较。

    strcat(s1,s2);连接 strcpy(s1,s2);复制

    strncat(s1,s2,n);将s2的前n个字符连接到s1后面,并返回s1。strncpy(s1,s2,n); 将s2的前n个字符复制给s1,并返回s1。

    如果必须使用C风格字符串,用strn的比strcat和strcmp更安全。如对const char *cp1 = “abc”; const char *cp2 = “cdef”;charlargeStr[3+4+2];strcpy(largeStr,cp1,4);

strcat(largeStr,” ”,2);strncat(largeStr,cp2,5);这边务必要记住每次拷贝、连接都要算上结束符null(一定要算上!)。每次拷贝和连接,原来的largeStr后面的null都会被新添加的字符串覆盖

       尽可能使用标准库类型string。如连接两字符串,直接用string largeStr = cp1; largeStr+=” “; largeStr+=cp2;对大部分应用而言,使用标准库类型string,除了增强安全性外,效率也提高了(这倒是新鲜)。因此应该尽量避免使用C风格字符串。

4.3.1、创建动态数组

    每个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区(free store)或堆(heap)。C用malloc和free;c++用new和delete。

    如int*pia = new int[10];(如果是内置类型(就是int,double等基本类型),则不会初始化,string等类类型会初始化。而不是在不在函数的意思!)创建数组后,new将返回指向数组第一个元素的指针。可以用int *pia = new int[10]();进行初始化。

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

    const对象的动态数组:const int *pia = new const int[10];是错误的,得初始化:const int *pia = new const int[10]();,而字符串可以像第一种写法这么写。但这const对象动态数组没啥用···

       调用new动态创建长度为0的数组是合法的。new返回有效的非零指针char arr[0];是错误的;char *cp = new char[0];是正确的。但cp解引用乱码,显然其没意义。

       务必要显示地将new出的存储空间回收。delete [] pia;遗漏空方括号会产生运行错误(得是类类型哎···,如果碰到int *pia = new int[10];delete piaVS中没有错误,换成char也没错误,这搞毛···)。没添空方括号对,至少会导致少释放了内存空间,从而产生内存泄露。

4.3.2、新旧代码的兼容

       const char *str = st3.c_str();用于返回指向字符数组首地址的指针,该数组存放了与string对象相同的内容,并且以结束符null结束。最好复制st3返回的数组,防止st3修改影响到str。

       使用数组初始化vector对象,如:

const size_t arr_size = 6;

int int_arr[arr_size] = {0,1,2,3,4,5};

vector<int> ivec(int_arr,int_arr+arr_size);第二个指针指向被复制的最后一个元素之后的地址空间。即初始化为第一个参数开始到第二个参数(且不包括第二个参数)。

4.4、多维数组

       也就是数组的数组。int ia[3][2] = {{1,2},{2,3},{3,4}};若元素有遗漏,按4.1.1规则初始化(不太对劲,只要有ia[3][2]= {{1},{},{}}{}写了无论是{}也好{{},{}}也可以,就会初始化(针对VS),如果只是ia[3][2]则会产生运行错误)。

       int (*ip)[2] = ia;

       注:int *ip[4];是指向int的指针;int (*ip)[4]是指向数组,该数组由4个int组成。

       可以用typedef简化指向多维数组的指针:

typedef intint_array[4];//一般不就是int array[4]这么声明么~~~~~~,就这么理解吧

int_array*ip = ia;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值