zoukankan      html  css  js  c++  java
  • C++语言中数组指针和指针数组彻底分析

    ################################
    #                              #
    #       基本知识               #
    #                              #
    ################################

    当然我们一切都是从最简单的内建类型开始,最后我会做一些推广。
    先看一下基本的形式,我们从这里起步!

    Cpp代码  
    1. --------------指针----------------  
    2. int a=10;  
    3. int *p=&a;  
    4.   
    5. -------------指针的指针-----------  
    6. int b=20;  
    7. int *p=&b;  
    8. int **p2p=&p;  
    9.   
    10. -------------简单数组-----------------  
    11. int c[10];//整数数组,含有10个整数元素  
    12.           file://也就是说每一个元素都是整数  
    13.            
    14. --------------指针数组--------------------  
    15. int *p[10];//指针数组,含有10个指针元素  
    16.             file://也就是说每一个元素都是指针  
    17.              
    18. --------------数组指针--------------------  
    19. int (*p)[10];//数组指针,这个指针可以用来指向  
    20.              file://含有10个元素的整数数组  

    上面这些简单的形式是我们必须要首先理解,这个是基本的知识。
    同时我们从上面也要得出一个很重要的知识提示:c++语言层面上
    关于变量声明的部分,后缀结合变量的优先级比前缀要高的。
    看我们上面的例子的最后两个就明白了,我们为了实现数组指针的
    声明我们不得不变通一下。我们采用()来实现优先级的改变,实现了
    数组指针的声明。

    ################################
    #                              #
    #      进一步提高知识          #
    #                              #
    ################################

    数组,数组的指针,指针的数组,概念太多了。我接受概念一多的
    时候,我就想把这些复杂的东西简单一下。因为我太懒了,概念简化
    一下,记住更容易一点。所以我们这里要认识一下上面这些概念本质。
    这样可以简化概念,减少记忆的难度。

    先看一段程序。

    Cpp代码  
    1. #include <iostream>  
    2. #include <typeinfo>  
    3. using namespace std;  
    4. int main()  
    5. {  
    6.  int vInt=10;  
    7.  int arr[2]={10,20};  
    8.    
    9.  int *p=&vInt;  
    10.  int **p2p=&p;  
    11.    
    12.  int *parr[2]={&vInt,&vInt};  
    13.  int (*p2arr)[2]=&arr;  
    14.   
    15.  cout<<"Declaration [int vInt=10] type=="<<typeid(vInt).name()<<endl;  
    16.  cout<<"Declaration [arr[2]={10,20}] type=="<<typeid(arr).name()<<endl;  
    17.  cout<<"Declaration [int *p=&vInt] type=="<<typeid(p).name()<<endl;  
    18.  cout<<"Declaration [int **p2p=&p] type=="<<typeid(p2p).name()<<endl;  
    19.  cout<<"Declaration [int *parr[2]={&vInt,&vInt}] type=="<<typeid(parr).name()<<endl;  
    20.  cout<<"Declaration [int (*p2arr)[2]=&arr] type=="<<typeid(p2arr).name()<<endl;  
    21.   
    22.  return 0;  
    23. }  

    运行的结果如下:(我在前面加了行号#XX)
    #01 Declaration [int vInt=10] type==int
    #02 Declaration [arr[2]={10,20}] type==int *
    #03 Declaration [int *p=&vInt] type==int *
    #04 Declaration [int **p2p=&p] type==int * *
    #05 Declaration [int *parr[2]={&vInt,&vInt}] type==int **
    #06 Declaration [int (*p2arr)[2]=&arr] type==int (*)[2]

    现在我们来分析一下结果。因为我们已经具有了第一部分的基本知识,我们现在
    可以很明确区别出来我们声明的类型。这里主要有两个很重要的部分,我们不过
    是就事讲事情,编译器是如何实现的原理不在这里讨论之列。

    --------#02:数组------------

    现在看#02,想到了什么没有呀?在编译器看来数组只是相对应类型的指针类型。
    当我们把数组传递给函数作为参数的时候,传递的是指针,所以我们可以利用
    参数来修改数组元素。这个转化是编译器自动完成的。

    void f(int[]);
    int a[2]={10,20};
    f(a);//这行等价于编译器完成的函数转化f(int *p)

    也就是说这里编译器自动完成了int[]类型到int *的转化,
    注意是编译器完成的,也可以说是语言本身实现的,我们
    对此只有接受的份了。

    -------#05:指针数组---------------

    指针数组的编译器内部表示也是对应类型的指针。

    ------#06:数组指针----------------
    数组指针的编译器内部表示就是有一点特别了。
    编译器(或者说是语言本身)有数组指针这个内部表示。
    由于c++语言的类型严格检查的语言(当然还有一些是存在隐式类型转化的)

    所以我们下面的写法是不能编译通过的。
    {
    file://---------编 译不能通过--------------
    int arr[3]={10,20};//注意是3个元素数组
    int (*p2arr)[2]=&arr;//注意是指向2个元素数组的指针
    file://---------编 译不能通过--------------
    }

    ################################
    #                              #
    #      初步小结                #
    #                              #
    ################################

    通过上面两个小节的内容,大家应该基本明白了,
    数组,指针,指针数组,数组指针到底是怎么一回事情了吧。

    -----------补充开始-----------------------
    关于数组和指针的转化,以及我们使用指针(++,--)等来操作数组,
    是基于数组在内存中是连续分布的。

    但是我们使用“迭代器”的时候,情况是不一样的。
    这个问题本文不讨论。

    -----------补充结束---------------------

    不过c++语言本身有很多诡异的地方(因为c++要考虑到跟c语言以及旧的c++版本兼容)。
    内建类型的这些性质特征到了函数部分会有一点小的变化,不过如果你了解了编译器做了
    什么以后的话,你也就不会太奇怪了。不过关于函数部分的内容我下次再说了。

    现在回到上面的内容。我们这里还是讲一下内建类型。显然一样类型的变量是可以互相赋值。
    不过当然还有一些其他情况也是可以的,比如类型的宽化,关于类的继承体系问题等等。

    当然了,不一样的类型一般来说是不能互相赋值,当然这里的例外就是强制转化,
    类的继承体系等情况了。

    看到这里就会明白下面的程序为什么会运行的了。
    我这里也把下面的程序作为今天内容的总结:

    Cpp代码  
    1. #include <iostream>  
    2. using namespace std;  
    3. int main()  
    4. {  
    5.  int a[2]={10,20};  
    6.  int *p=a;//根据上面说明,由于编译器的参与,两者类型转化后一致  
    7.    
    8.  int vInt=10;  
    9.  int *parr[2]={&vInt,&vInt};   
    10.  int **p2p=parr;//上面分析,类型一致  
    11.    
    12.  return 0;  
    13. }  
  • 相关阅读:
    LeetCode——Generate Parentheses
    LeetCode——Best Time to Buy and Sell Stock IV
    LeetCode——Best Time to Buy and Sell Stock III
    LeetCode——Best Time to Buy and Sell Stock
    LeetCode——Find Minimum in Rotated Sorted Array
    Mahout实现基于用户的协同过滤算法
    使用Java对文件进行解压缩
    LeetCode——Convert Sorted Array to Binary Search Tree
    LeetCode——Missing Number
    LeetCode——Integer to Roman
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/4518805.html
Copyright © 2011-2022 走看看