zoukankan      html  css  js  c++  java
  • C++中的数组,指针解析

    哎,由于上课的时候没有听课,导致指针这里真的是似懂非懂,虽然对写代码影响不大,但是总是精确度下了一个档次,所以今天趁机把这写个玩意儿弄明白点,省的以后每次都得查。咱们不求文章长,只求内容清楚~

     如果有谁看出了里面有什么问题,请帮忙指出来,免得误导别人~饺子在此先说声谢谢了~


    其实关于数组比较好掌握,就是分配某种类型的一个组,这个组的长度必须给出或者初始化,我一开始觉得很不方便,因为有很多情况下我们不知道数组的长度,比如这个数组要由用户定义之类的情况,但是没办法,电脑要为这个数组分配一个内存空间,你不告诉电脑,电脑编译的时候就迷茫了,得出错,所以就互相迁就吧,给他个长度,或者给个初始值,这都是必要的。

    数组的声明:简单来说,数组是从低维,0开始存放的,举个例子  int a[2][2] 在内存里的存放顺序就是:a[0][0] - a[0][1] - a[1][0] - a[1][1]. 额,因为就俩数字所以看起来像二进制数数,反正就这个道理。声明的时候,要么就给出中括号里面的数字,要么就给个初始化,比如 int a[2][2];   或者int a[][2]={1,1,1,1};当然这个等效于int a[][2]={{1,1},{1,1}};  注意,低维的数字不能省,这个‘[2]’里的2不可以省。

    接下来是指针

    首先要知道的是内存的访问方式,一般来说我们都是通过变量名来访问内存的,比如int a=1; 我们通过a来访问这个存放1的内存,但是还有一种方式是通过地址来访问。打个比方,学校就是我们的内存,每个学生都有自己的名字,以及对应的学号,那么名字就是这里的变量名,而学号就是地址,在批量处理,要传输大量数据的时候,通过学号当然比通过名字访问要方便,所以两个方式各有千秋,必须都掌握好。

    指针: 指针其实是一种数据类型,具有指针类型的变量成为指针变量,指针变量用来存放内存单元地址。声明方法如下:

    //数据类型 * 标识符
    char * c;

    这里的*就告诉电脑这是个指针变量,前面的数据类型只表示指针所指地址的数据是那种类型,也就是说,c是指针,不是char(说了句废话。。)。

    可以这么理解,'*' 相当于我们定义一个变量int a=3;时的'int', 至于为什么要声明内存的数据类型(也就是为什么'*'前要有'char'),原因很简单,不同的数据类型占有的内存单元长度不同,比如是个short类型的指针,那么内存占俩个字节,但是如果是long的话,就占4个字节,所以要有数据类型,告诉指针取几个字节。

    感谢8楼的总结,我在这里引用一下:

    指针,是一个内存读取器类。
    可以按照给定的数据类型定义和地址位置返回期望的数据值。

    数组,是对指针的再次封装。
    包含一个连续内存块以及一个指针常量。

    关于'*'和'&'

    '*'的意思之前也讲过了,就是个指针运算符。‘&’的名字叫取地址运算符。区别可以这么说:

    int * p; //定义了一个int型指针。
    int a;
    p=&a; // p等于a的地址,也就是说&这个符号表示‘取地址’

    ‘&’ 这个符号还可以这么用:int &y; 表示声明一个int型的引用 y。

    给指针赋值可以像上面↑那样,先声明,后赋值,也可以是声明的时候直接赋值,效果雷同给普通变量赋值。注意,对于数组,可以直接用数组名,因为数组名就是它的地址:

    int a[5];
    int *p=a;//不需要'&',因为a本身即是一个地址了。

    使用指针是,如果直接使用,出来的是地址,加个'*',出来的是内容,具体这么表示吧:

    int a=5;
    int *p=&a;
    cout<<p<<endl; // 这一行将输出a的地址
    cout<<*p<<endl; // 这一行将输出数字'5'

    声明的时候要注意:

    • 可以声明指向常量的指针,但是不要妄图通过指针来改变这个常量值╮(╯_╰)╭,不过指针不像常量那么有节操,指针可以改变所指对象的值。如下:
      const char * p="HELLO"; // 这个const是形容char的
      char s[]="HI";
      p=s; //这么写是可以的,就是相当于给p一个新的地址,

      *p='HI'; // 这就要出错了,都说了人家char是个常量了,不要改人家嘛。

      //但是:
      char *p="HELLO";
      *p='H'; //这个编译可以通过,但是运行出错了。因为违反了指针的规则。
      利用这个特点,可以保证指针所指向的常量不被意外更改。
    • ‘*’放在const前面的话,表示这个指针是个常量,指针本身不能被更改。下面这种情况 line2是不被允许的说~
      char * const p="abc";
      p="def"; // nonono,要出错咯~
       
    • 虽然我很讨厌规矩,但是编程嘛,必须按着规矩来,定义了什么类型的指针,就赋什么类型的值,咱不能给int型指针赋个'x'是吧。不过总有那么几个叛逆的,所以就有了种叫做void类型的指针,通过强制转换,可以访问任何类型的数据。void指针的使用:
      void *vp;  // 一个void类型的指针
      int a=5;
      int *px;
      vp = &a;
      px = (int *)vp;//把vp的void * 强制转换成 int *,好满足px的类型。


    指针的运算:

             指针可以进行加、减运算,规则其实很容易理解。比如我定义了一个short型指针p,占4个字节,那么p+1,就是地址往后数4个字节,一般是给数组用的。比如:

    int a[]={2,3,4,5};
    int *p=a;
    cout<<*(p+1)<<endl; //输出就是3

    指针型函数:

    这个东西是大大的好啊,可以让返回值从一个扩展的大批的数据,具体用法就是返回类型后面加个"*",用起来很爽。同样来点内容免得光说不练。

    int * getAll(int *a){
    .....
    // 处理了半天,然后返回
    return a;
    }

    (这里谢谢@陈梓瀚(vczh)的指正)

    这样返回的就是四个数字而不是一个数字了,可以通过返回的指针,进行运算,得到这批数据,并且可以保护这批数据。

    指向函数的指针

    其实函数和数组一样,在内存中从函数名开始存起,也就是说知道了函数名,接下来的内存里放的就是函数体了,所以,既然指针可以指向数组,当然也可以指向函数了。形式就是: 数据类型 (* 函数指针名)(形参表)   如果一个函数的   返回值类型=指针数据类型  形参表=指针形参表,那个这个函数指针就可以指向这个函数了。

    void function(int a,int b);
    void function2(int x,int y);
    void (* fpointer)(int,int); //必须和要指向的函数形参类型,个数,顺序完全相同,才能指向这个函数

    //使用时:
    function(5,5);
    // 上下 两者效果等同
    fpointer=function;
    fpointer(6,6);

    fpointer=function2; // 多好啊还可以指向别的函数,只要条件符合。

    对象指针:(话说这部分其实很废柴啊囧rz)
     就是指向一个类的指针 语法: 类名 * 指针名;   调用成员时: 指针名->成员名; 注意在给指针赋值前,要对这个对象进行初始化~

    Clock c(1,2,4);
    Clock *p;
    p=&c; //这里c必须是初始化好的。否则不可以使用指针p
    p->getTime();


    指向类的非静态成员的指针:在类的外部定义一个指针指向类得公有成员时,定义如下:

    如果是公有数据成员:

    Clock  clock(1,1,2);
    Clock *pClock = &clock;
    //定义
    int Clock::*pHour;

    //赋值:
    pHour=&Clock::Hour;

    //调用:(三个效果相同)
    clock.*pHour;
    pClock->Hour;
    clcok.Hour;

    如果是公有函数成员:

    Clock  clock(1,1,2);
    Clock *pClock = &clock;
    //定义: 返回类型 (类名::*指针名)(参数表)
    int (Clock::*pGetHour)();

    //赋值:
    pGetHour= &Clock::GetHour;

    //调用:(三个效果相同)
    clock.*pGetHour;
    pClock->GetHour;
    clcok.GetHour;


    到这里我已经头晕了,不知道耐心看到这里的同学们头晕了没?晕了的话喝口水,刷刷微博先,劳逸结合嘛~

    动态内存分配:(哇,听起来好高端)7行代码概括:

    int *point;
    point = new int(2); //分配动态存储空间并把数值2放入内存
    // .....
    delete point;
    /*------------------------------------------*/
    Clock *pclock = new Clock[2]; //此时不可以初始化
    pclock[0].Set(1,2,3);//就假设有这么个函数呗
    pclock[1].Set(4,5,6);
    delete[] pclock; //就是为了说,这里delete后面要有‘[]’


    浅拷贝和深拷贝:(最后一点儿啦  加油~)

    其实这个用代码写起来复杂,但是说起来很简单。

    如果一个类里面有个成员p是个指针,定义了实体对象clock, clock2,然后clock中的p指向一个存着int型数据的内存空间。当我们仅仅用clock2.p=clock.p来给clock2中的p赋值的话,其实两个实体对象的指针p就指向了同一个内存空间,这样的话,对这个内存空间的数字进行操作的时候,就会同时影响到clock和clock2.这就是浅拷贝。

    深拷贝,就是让它不印象呗。所以clock2的p如果这么拷贝:p=new int(*clock.p); 那么俩玩意儿就互不影响了,如此这般,就是深拷贝。

    呼,终于结束了,玩儿微博去也~

  • 相关阅读:
    网站架构探索(3)负载均衡的方式
    架构师之路(6)OOD的开闭原则
    也谈IT人员流失问题 王泽宾
    技术体系的选择之Java篇
    网站架构探索(2)CDN基本常识
    设计模式之单例模式
    网站架构探索(1)序言 王泽宾
    架构师之路(39)IoC框架
    发展之道:简单与专注
    修me30打印机
  • 原文地址:https://www.cnblogs.com/jiaozihardworking/p/2343404.html
Copyright © 2011-2022 走看看