• C语言指针


    C语言指针

    C语言指针

    1.指针与内存地址的区别:

    指针是带有解释方式的内存地址,它不但指出了数据存放在何处,还指出了这个内存
    地址存放的是什么类型的数据,而内存地址仅仅是个地址,至于数据是什么类型,数据
    有多大,并没有解释。
    

    2.指针访问和数组下标访问谁快谁慢:

    使用指针访间接访问的程序运行速度要比使用数组下标访问快,这是不正确的,  
    使用指针间接访问内存数据速度要小于等于使用数组下标访问,这是因为使用下
    标访问时,由CPU先算出要访问数据的内存地址,然后进行一次内存访问,就可以  
    得到指定内存地址处的数据,而是用指针间接访问时,要先进行一次内存访问,  
    拿出指针所指向的内存地址,然后得到该内存地址后,在进行一次访存才能得到  
    指定内存地址处的数据,共两次访问内存,所以通过数组下标访问要比使用指针  
    间接访问要快。
    

    3.指针使用注意事项

    定义指针时要赋合适的初值,如果没有合适的初始值,那就赋间接访问时能发生错误
    的值,一般赋NULL,在新的C++11标准中规定了关键字nullptr表示空指针。
    指针使用完后要置无效,使用指针时一定要让其处于可控状态。
    

    4.void类型指针

    void *型指针仅仅只有内存地址值,没有类型信息,不能做下标运算
    void*型指针可以接受任何类型的指针,将一个有类型指针赋给void*型指针时,只保留
    内存地址,类型信息丢失;但一个void*型指针如果不经强转是不能赋值给其它类型的
    指针,void*型指针通常作为复制数据函数的形参,如C库函数memcpy的形参就是void*
    类型。
    

    5 指针运算

    • 指向基本数据类型的指针,加减运算后得到同类型指针
      设有指针Type* ptr=....; Type为基本数据类型,N为整数
      那么ptr+N=(Type*)((int)ptr+sizeof(Type)*N);

    • 两个同类型指针相加无意义,但两者相减结果的绝对值为两者之间间隔的同类型数据的个数
      例:
      指针相减.png
      下面观察下内存:
      指针相减1.png
      由于在Debug版下指针pA和pB之间间隔了8字节,由于pB所在的内存地址比pA要低
      所以相减后得到-3,如果是pA-pB将得到3,所以可以推出两指针相减的公式:
      设有指针Type* ptr1,*ptr2:
      ptr1-ptr2 = ((int)ptr1-(int)ptr2)/sizeof(Type);

    • 指针不能进行乘除运算,编译时无法通过

    常量指针与指针常量以及指向常量的常指针

    • 常量指针
      无法使用该指针修改它所指向数据,但可以改变该指针的指向,使它指向另一个变量;
      定义时const关键字在""号左边,则定义的是常量指针,形如const Type * 或者
      Type const
      ;

    • 指针常量
      该指针本身是个常量,定义时必须进行初始化,无法修改该指针的指向,但是可以
      修改该指针所指向变量的数据
      定义时const关键字在"*"号右边,则定义的是指针常量,形如Type * const;

    • 指向常量的常指针
      无法使用该指针修改它所指向的数据,也无法修改该指针的指向
      定义时"*"左右都有const关键字,形如Type const * const或者const Type *const;

    指针数组与数组指针的区别

    • 指针数组
      数组中每个元素都是指针,例如:int* PtrAry[8];
    • 数组指针
      一个指向数组的指针,例如:int nAry[M][N]; 定义一个指向该数组的
      指针: int (*pAry)[N] = &nAry;

    数组元素内存地址的计算

    计算数组元素的内存地址时要熟练记住一下规则:

    • 规则1:[数组名]是[数组第0个元素类型]的地址常量
    • 规则2:[某类型]指针取内容得到[某类型]
    • 规则3:[某类型]指针下标运算得到[某类型]
    • 规则4:[某类型]指针和整型相加得到[某类型]指针常量
    • 规则5:[某类型]指针减去[某类型]指针得到两者之间间隔的同类型元素的个数
    • 对于多维数组,例如:int nAry[3][4][5],我们可以把它视为一维数组,元素类型为int[4][5]类型
      例:
     int nAry[2][3]=
     {
         {10,20,30},
         {40,50,60}
     };
    
     int(*pAry)[3] = nAry;
     int *pInt = nAry[0];
    
     printf("%08X
    ", nAry);
     printf("%08X
    ", nAry[0]);
     printf("%08X
    ", nAry[0][0]);
     printf("%08X
    ", pAry[0]);
     printf("%08X
    ", *pAry[0]);
     printf("%08X
    ", pAry[1]);
     printf("%08X
    ", *pAry[1]);
     printf("%08X
    ", (*pAry)[1]);
     printf("%08X
    ", pAry + 1);
     printf("%08X
    ", *pAry + 1);
     printf("%08X
    ", (*pAry)[1] + 1);
     printf("%08X
    ", *(pAry[1]) + 1);
    

    C语言指针_数组首地址.png
    如上图所示,在监视窗口中可以看的数组nAry的首地址为0x00EFF7F0:

    • nAry是数组名,其值为数组首地址,所以printf("%08X ", nAry)输出的结果就是00EFF7F0

    • nAry是数组名,指向nAry[2][3]中第0个元素,第0个元素类型为int[3],根据规则3可知nAry[0]指向一个
      int[3]类型,这是一个int类型的一维数组,所以nAry[0]int类型指针,该指针指向nAry[2][3]中的元素10
      因为元素10位于数组首地址处,所以nAry[0]指向的内存地址为0x00EFF7F0,printf("%08X ", nAry[0])
      输出结果为00EFF7F0

    • nAry[0][0]做了两次下标运算,由上一步可知nAry[0]为int类型指针,那么根据规则3,nAry[0][0]得到int
      类型,所以printf("%08X ", nAry[0][0])输出0000000A

    • pAry指向数组nAry,pAry指向元素为int[3]类型的数组,故pAry是一个int[3]类型指针,pAry[0]得到int[3]
      类型,int[3]为一维数组,所以pAry[0]为int类型指针,指向一维数组{10,20,30}中的元素10,而元素10位于
      数组首地址处,所以printf("%08X ", pAry[0])输出结果为00EFF7F0

    • 由上面的推导可知pAry[0]是一个int类型的指针,实际上它指向的就是{10,20,30}中的元素10,根据规则
      "[某类型]指针取内容得到[某类型]",对pAry[0]取内容得到10,所以printf("%08X ", *pAry[0])
      输出结果为0000000A

    • pAry是一个int[3]类型的指针,pAry[1]指向数组nAry[2][3]中第1个元素,所以pAry[1]指向的内存
      地址值为:00EFF7F0+sizeof(int[3])=00EFF7FC,所以printf("%08X ", pAry[1])输出结果为00EFF7FC

    • pAry[1]是一个int类型的指针,指向数组nAry中第1个元素也就是{40,50,60}中的40,那么该类型指针
      取内容得到int类型,也就是40,所以printf("%08X ", *pAry[1])输出00000028

    • 再来看看(*pAry)[1],因为(*pAry)加了"()",所以优先计算pAry,pAry是一个int[3]类型指针,根据
      规则2,对pAry取内容得到int[3]类型,所以
      pAry是int类型指针,根据规则3可知(*pAry)[1]得到
      {10,20,30}中的20,printf("%08X ", (*pAry)[1])输出结果为00000015

    • pAry是一个int[3]类型指针,那么根据规则4可知pAry+1得到一个int[3]类型指针,指向的内存地址为
      00EFF7F0 + sizeof(int[3])=00EFF7FC,所以printf("%08X ", pAry + 1)输出00EFF7FC

    • pAry是一个int[3]类型指针,根据规则2可知,*pAry得到一个int类型指针,在根据规则4可知,*pAry+1
      得到一个int类型指针,指向的内存地址为: 00EFF7F0 + sizeof(int) = 00EFF7F4,所以
      printf("%08X ", *pAry + 1)输出00EFF7F4

    • *pAry为int类型指针指向{10,20,30}中的元素10,根据规则3可知(*pAry)[1]取得{10,20,30}中的20
      所以printf("%08X ", (*pAry)[1] + 1)输出00000015

    • pAry[1]为int类型指针,指向{40,50,60}中的40,根据规则2可知*(pAry[1])为40,所以
      printf("%08X ", *(pAry[1]) + 1)输出00000029

    结果验证:
    C语言指针_运行结果.png

    二级指针

    指向一级指针的指针称为二级指针,例如int**p=NULL;就是定义了一个二级指针,二级指针可作为函数
    参数传入,函数内部可通过间接访问,来修改二级指针所指向的一级指针,使用场景较少,至于三级指针
    和其它的多级指针则几乎没有使用场景.

    函数指针

    函数指针的声明应该包括三部分:返回值类型 (调用约定*标识符)(参数类型列表)
    例:

    int _stdcall Test(int nAry[], char ch)
    {
      return nAry[0] * ch;
    }
    

    定义函数指针为:int (_stdcall * pfnTest)(int*,char);

    C语言指针_函数指针.png

  • 相关阅读:
    Spring+SpringMVC+MyBatis深入学习及搭建(二)——MyBatis原始Dao开发和mapper代理开发
    Spring+SpringMVC+MyBatis深入学习及搭建(一)——MyBatis的基础知识
    Hibernate HQL语句
    spring的IO原理
    jsp概述
    java的常用接口
    java各种内部类
    Serlvet 处理http请求并保持长连接
    JVM高级特性与实践(一):Java内存区域 与 内存溢出异常
    JVM高级特性与实践(二):对象存活判定算法(引用) 与 回收
  • 原文地址:https://www.cnblogs.com/UnknowCodeMaker/p/11022291.html
Copyright © 2020-2023  润新知