精华内容
下载资源
问答
  • 数组引用 C 数组做参数 深入分析

    千次阅读 2018-11-06 15:22:26
    数组引用 C 数组做参数 深入分析

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                   

    "数组引用"以避免"数组降阶"(本文曾贴于VCKBASE\C++论坛)

    受[hpho]的一段模板函数的启发,特写此文,如有雷同,实在遗憾。
    数组降阶是个讨厌的事,这在C语言中是个无法解决的问题,先看一段代码,了解什么是"数组降阶"

    #include <IOSTREAM>
    using namespace std;

    void Test( char array[20] )
    {
        cout << sizeof(array) << endl; // 输出 4
    }

    int main( void )
    {
        char array[20] = { 0 };
        cout << sizeof(array) << endl; // 输出 20
        Test( array );
    }

    为什么同样申明的array一个输出20一个输出4?这是因为void Test( char array[20] )中的array被降阶处理了,void Test( char array[20] )等同于void Test( char array[] ),也等同于void Test( char* const array ),如果你BT(开玩笑),它也等同于void Test( char array[999] )。
    就是说
    void Test( char array[20] )
    {
        cout << sizeof(array) << endl;
    }
    被降成
    void Test( char* const array )
    {
        cout << sizeof(array) << endl; // 既然是char*,当然输出4
    }
    这样一来问题大了,你完全可以定义一个不足20个元素的数组,然后传给Test,坐等程序崩溃。在一些要求较高的场合就不能使用数组做参数,真TMD心有不甘。

    那么在C语言中怎样解决这个问题?
    没办法,应该说没有好办法。a:做个结构,其中仅一个char array[20],然后用这个结构指针代替char array[20]。可见这是个很繁琐的办法,且不直观;b:在Test内部使用_msize来计算array长度。这更不行,首先它使得错误的发现被推迟到运行期,而不是编译期,其次_msize长度/元素大小>=array长度,也就是说就是new char[19]和new array[20]分配的大小是一样的,这样一来,虽不至于导致程序崩溃,但运算结果却不正确。

    感谢[hpho],受其启发,C++中有C所没有的"引用",但数组引用是怎样申明的呢?经过几番试验,Look

    #include <IOSTREAM>
    using namespace std;

    void Test( char (&array)[20] ) // 是不是很像 char *p[20] 和 char (*p)[20] 的区别?
    {
        cout << sizeof(array) << endl;
    }

    int main( void )
    {
        char array[20] = { 0 };
        cout << sizeof(array) << endl;
        Test( array );
    }

     

     

    在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指针。

    例如,如下声明 :
    void putValues( int[ 10 ] );
    被编译器视为 
    void putValues( int* );
    数组的长度与参数声明无关,因此,下列三个声明是等价的:
    // 三个等价的 putValues()声明
    void putValues( int* );
    void putValues( int[] );
    void putValues( int[ 10 ] );

     

    因为数组被传递为指针 所以这对程序员有两个含义:


    1. 在被调函数内对参数数组的改变将被应用到数组实参上而不是本地拷贝上,当用作实参的数组必须保持不变时,程序员需要保留原始数组的拷贝函数可以通过把参数类型声明为 const 来表明不希望改变数组元素。
    void putValues( const int[ 10 ] );

    2.  数组长度不是参数类型的一部分,函数不知道传递给它的数组的实际长度,编泽器也不知道,当编译器对实参类型进行参数类型检查时,并不检查数组的长度。例如:
    void putValues( int[ 10 ] ); // 视为 int*

    int main() {
    int i, j[ 2 ];
    putValues( &i ); // ok: &i 是 int*; 潜在的运行错误
    putValues( j ); // ok: j 被转换成第 0 个元素的指针
    // 实参类型为 int*: 潜在的运行错误
    return 0;
    }

    参数的类型检查只能保证putValues()的两次调用都提供了int*型的实参,类型检查不能检验实参是一个 10元素的数组 。习惯上, C风格字符串是字符的数组,它用一个空字符编码作为结尾。但是所有其他类型,包括希望处理内含空字符的字符数组必须以某种方式在向函数传递实参时使其知道它的长度。

    一种常见的机制是提供一个含有数组长度的额外参数。例如:
    void putValues( int[], int size );
    int main() {
           int i, j[ 2 ];
           putValues( &i, 1 );
           putValues( j, 2 );
           return 0;
    }

    另外一种机制是将参数声明为数组的引用

    当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。 
    // 参数为 10 个 int 的数组
    // parameter is a reference to an array of 10 ints
    void putValues( int (&arr)[10] );//不能写成&arr[10],因为下标操作符的优先级较高
    int main() {
           int i, j[ 2 ];
           putValues( i ); // 错误: 实参不是 10 个 int 的数组
           putValues( j ); // 错误: 实参不是 10 个 int 的数组
           return 0;
    }

     

    "数组引用"以避免"数组降阶"(本文:http://blog.vckbase.com/bruceteen/archive/2004/05/20/232.aspx

    受[hpho]的一段模板函数的启发,特写此文,如有雷同,实在遗憾。
    数组降阶是个讨厌的事,这在C语言中是个无法解决的问题,先看一段代码,了解什么是"数组降阶"

    #include <IOSTREAM>
    using namespace std;

    void Test( char array[20] )
    {
         cout << sizeof(array) << endl; // 输出 4
    }

    int main( void )
    {
         char array[20] = { 0 };
         cout << sizeof(array) << endl; // 输出 20
         Test( array );
    }

    为什么同样申明的array一个输出20一个输出4?这是因为void Test( char array[20] )中的array被降阶处理了,void Test( char array[20] )等同于void Test( char array[] ),也等同于void Test( char* const array ),如果你BT(开玩笑),它也等同于void Test( char array[999] )。
    就是说
    void Test( char array[20] )
    {
         cout << sizeof(array) << endl;
    }
    被降成
    void Test( char* const array )
    {
         cout << sizeof(array) << endl; // 既然是char*,当然输出4
    }
    这样一来问题大了,你完全可以定义一个不足20个元素的数组,然后传给Test,坐等程序崩溃。在一些要求较高的场合就不能使用数组做参数,真TMD心有不甘。

    那么在C语言中怎样解决这个问题?
    没办法,应该说没有好办法。a:做个结构,其中仅一个char array[20],然后用这个结构指针代替char array[20]。可见这是个很繁琐的办法,且不直观;b:在Test内部使用_msize来计算array长度。这更不行,首先它使得错误的发现被推迟到运行期,而不是编译期,其次_msize长度/元素大小>=array长度,也就是说就是new char[19]和new array[20]分配的大小是一样的,这样一来,虽不至于导致程序崩溃,但运算结果却不正确。

    感谢[hpho],受其启发,C++中有C所没有的"引用",但数组引用是怎样申明的呢?经过几番试验,Look

    #include <IOSTREAM>
    using namespace std;

    void Test( char (&array)[20] ) // 是不是很像 char *p[20] 和 char (*p)[20] 的区别?
    {
         cout << sizeof(array) << endl;
    }

    int main( void )
    {
         char array[20] = { 0 };
         cout << sizeof(array) << endl;
         Test( array );
    }

     

    本文来自:http://blog.vckbase.com/smileonce/archive/2004/11/05/1295.aspx

    去年,周星星大哥曾经在VCKBASE/C++论坛发表过一篇文章《"数组引用"以避免"数组降阶"》*1,当时我不能深入理解这种用法的含义;时隔一年,我的知识有几经锤炼,终于对此文章渐有所悟,所以把吾所知作想详细道来,竟也成了一篇文章。希望本文能对新手有所启迪,同时也希望大家发现本文中的疏漏之处后不吝留言指教。

    故事起源于周星星大哥给出的两个Demo,为了节省地方,我把两个Demo合二为一,也能说明同样的问题:

    #include using namespace std;void Foo1(int arr[100]){ cout << "pass by pointer:   " << sizeof(arr) << endl;}void Foo2(int (&arr)[100]){ cout << "pass by reference: " << sizeof(arr) << endl;}void main(){ int a[100]; cout << "In main function : " << sizeof(a) << endl; Foo1(a); Foo2(a); }

    其运行结果如下:

    In main function : 400
    pass by pointer: 4
    pass by reference: 400

    这段代码说明了,如果数组形参是数组名形式(或者指针形式,下文讨论)时,使用sizeof运算符,将得不到原来数组的长度;如果用传递原数组引用的方法,则没有问题。

    这段代码的确很难理解,因为这短短的十几行涉及到了形参与实参的关系、数组名和指针的关系、引用的意义、声名和表达式的关系这4大类问题,只要有1条理解不透、或者理解不正确,就理解不透上面的这段代码。本文也就从这4个问题入手,把这4个问题首先解决掉,然后再探讨上面的这段代码。虽然这样看来很是繁复,但是我认为从根上入手来理解、学习,是条似远实近的道路。

    一、函数形参和实参的关系

    void Foo(int a);Foo(10);

    这里的a叫做形式参数(parameter),简称形参;这里的10叫做实际参数(argument),简称实参。形参和式参之间是什么关系呢?他们是赋值的关系,也就是说:把实参传递给形参的过程,可以看作是把实参赋值给形参的过程。上面的例子中,实参10传递给形参a,就相当于a=10;这个赋值的过程。(因为数据类型多的很,无法举例子举全面,所以这里就不举例子了;如果觉得不好理解,就在vc中写个sample调试一下各种数据类型的情况,你就能够验证这个结论了。)

    二、数组名和指针的关系

    这个问题是个历史性的问题了,在C语言中,数组名是当作指针来处理的。更确切的说,数组名就是指向数组首元素地址的指针,数组索引就是距数组首元素地址的偏移量。理解这一点很重要,很多数组应用的问题就是有此而起的。这也就是为什么C语言中的数组是从0开始计数,因为这样它的索引就比较好对应到偏移量上。在C语言中,编译过程中遇到有数组名的表达式,都会把数组名替换成指针来处理;编译器甚至无法区分a[4]和4[a]的区别!*2但是下面这一点需要注意:

    int a[100];int *b;

    这两者并不等价,第一句话声明了数组a,并定义了这个数组,它有100个int型元素,sizeof(a)将得到整个数组所占的内存大小,是400;第二句话只是声明并定义了一个int型的指针,sizeof(b)将得到这个指针所占的内存大小,是4。所以说,虽然数组名在表达式中一般会当作指针来处理,但是数组名和指针还是有差距的,最起码有a==&a[0]但是sizeof(a)!=sizeof(a[0])。

    并且在ANSI C标准中,也明文规定:在函数参数的声明中,数组名北边一起当作指向该数组第一个元素的指针。所以,下面的几种书写形式是等效的:

    void Foo1(int arr[100]){}void Foo2(int arr[]){}void Foo3(int *arr){}

    C++尽可能的全面兼容C语言,所以这一部分的语法相同。

    三、引用的意义

    “引用“是C++中引进的概念,C语言中没有。它的目的在于,在某些方面取代指针。如果你认为引用和指针并无大不同,肯定会为指针报不平,颇有一种“即生亮何生瑜”的感慨;但是,引用确实有新的特色,也确实在很多地方的表现和指针有所不同,本文就是一例。使用引用,我们要把握这它最最最重要的一点,这也是它和指针最大的区别:引用一经定义,就和被它引用的变量紧紧地结合在一起,再不分开,对引用的任何操作都反映在它引用的变量上;而指针,只是访问它指向变量的另一种方式,两者虽有联系,但是并不像引用那样密不可分。:)

    #include using namespace std;void main(){ int a = 10; int & a_ref = a; int b = 20; //int & b_ref ; // error C2530: 'b_ref' : references must be initialized                 // 定义引用时就要初始化,说明引用跟它指向的元素密不可分 int & b_ref = b; int * p; int * q; //下面的结果证明了:引用一经定义,就不能再指向其他目标;  //把一个引用b_ref赋值给另一个引用a_ref,其实就是把b赋值给了a. cout << a_ref << " " << b_ref << endl; a_ref = b_ref; cout << a_ref << " " << b_ref << endl; cout << a << " " << b << endl; cout << endl;  //即使对一个引用a_ref取地址,取得也是a的地址。已经“恶鬼附体”了:) p = &a; q = &a_ref; cout << p << " " << q << endl; cout << endl;  //下面这段代码展示了指针与引用的不同 p = &a; q = &b; cout << p << " "<< q << endl;  p = q; cout << p << " "<< q << endl; cout << endl; system("pause");}

    下面是运行的结果,以供参考:

    10 20
    20 20
    20 20

    0012FED4 0012FED4

    0012FED4 0012FEBC
    0012FEBC 0012FEBC

    四、声明和表达式的关系

    这里想说明的是,分析一个声明可以把它看作一个表达式,按照表达式中的运算符优先级顺序来声明。比如int (&arr)[100],你首先要找到声明器arr,那么&arr说明arr是一个引用。什么引用呢?在看括号外面,[]说明了这一个数组,100说明这个数组有100个元素,前面的int说明了这个数组的每个元素都是int型的。所以,这个声明的意思就是:arr就是指向具有100个int型元素的数组的引用。如果你觉得这种理解很晦涩,那你就不妨用typedef来简化声明中的复杂的运算符优先级关系,比如下面的形式就很好理解,其效果是和最初的那个例子是一样的:

    #include using namespace std;typedef int INTARR[100]; //这个,这个...也可以用表达式来理解,有点“GNU is not UNIX“的味道是吧?void Foo(INTARR &arr)    //noh,这样看就很明白了,就是传了个引用进去{ cout << "pass by reference: " << sizeof(arr) << endl;}void main(){ INTARR a;            //用类型别名来定义a INTARR &a_ref=a;     //用类型别名来定义引用a_ref  cout << "In main function : " << sizeof(a) << endl; Foo(a);  system("pause");}

    ===大结局===

    吐沫星乱飞了半天,大家感觉还好吧,快结束了,大家再忍耐一下。看看下面这段程序:

    #include using namespace std;void main(){ int a[100]; int * pa = a; int (&a_ref)[100] = a; cout << sizeof(a) << endl; cout << sizeof(pa) << endl; cout << sizeof(a_ref) << endl; system("pause");}

    怎么样,是不是对输出结果感到很自然呢?如果是,那就好办了。我总结一下就下课哈!^_^ 数组名在表达式中,往往被当作是指向首元素a[0]地址的指针,但是在sizeof(a)中,返回的结果是数组a占用内存的大小;pa是指向a的指针,他也指向a[0],但是sizeof(pa)中,返回结果是pa这个指针所占内存空间的大小,之所以这样,因为pa这个指针和数组a的结合不够紧密,属于访问数组a的第二被选方案;a_ref这个引用,就是对数组a的引用,就像“恶鬼附体”一样,一旦附体附上了,你怎么也甩不掉它,对它的任何操作,全部都反映在a上。在看本文最初的那个例子,比这个例子所增加的操作就是函数实参到形参的传递,我们在上面说过了,从实参到形参的传递可以看作是把实参赋值给形参。所以本文最初的那个例子,其实际的操作过程就和本文最后的这个例子是一样的。所以,并非函数把数组给“降阶”了,而是它原原本本就该这样,千万不必奇怪。 :p

    意犹未尽,在PS一段:在C语言中,没有引用,是怎么解决这种问题呢。下面是常用的几种作法:

    1.传递数组的时候,在增加一个参数,用来记录数组的元素个数或者长度。main(int argc, char ** args)就是这种做法;这种方法还可以防止溢出,安全性比较高。
    2.在数组的最后一个有效元素后面作一个标志,指明数组已经结束了。C语言中用char数组表示字符串,传给相关的字符串函数,用的就是这种做法。这种方法保证了C的所谓字符串是无限长度的,因为用一个变量表示数组的长度的话,终归会受到这个变量类型的限制,比方说这个变量是unsigned byte型的,那么字符串长度就不能超过256,否则这个变量就溢出了。
    3.对于多维数组,通常的方法是在最后一个有效维后面做一行标志,比如a[3][3]={{1,0,2},{2,2,5},{-1,-1,-1}}。如果我的程序用不到-1,我可以拿-1来填充最后一行,作为标志。这样在函数内部检测到某一维的元素都是-1,就说明到底了。

    方法是灵活多变的,关键看人怎么用了。C老爹Dennis Ritchie曾经说过:C诡异离奇,缺陷重重,却获得了巨大的成功。

    注1:本文将不再引用“降阶”这个术语,原因是我认为这个“降阶”的概念有种把类似2维数组压扁到1维的意思,其实本文讨论的并不是这个问题,本文讨论的是数组形参传递过程中数组长度损失的问题(这么说也不准确,还是看文中的讨论吧)。

    注2:C语言的编译器遇到数组元素arr[i],就会替换成*(arr+i)的形式。

     

     

    int main(int argc, char* argv[])
    {
    int arr1[10] = {4,8,3,5,98};
    for (int i=0;i<10;i++)
    {
       cout<<arr1[i]<<" ";
    }
    cout<<endl;
    for (int i=0;i<10;i++)
    {
       cout<<i[arr1]<<" ";
    }
    cout<<endl;
    return 0;
    }

    第一次知道C++竟然不能区分这样的数组元素arr1[i]和i[arr1].两个循环都可以输出数组arr1的元素。刚知道,差点被雷倒!!

     

    1.变长一维数组  
       
      这里说的变长数组是指在编译时不能确定数组长度,程序在运行时需要动态分配内存空间的数组。实现变长数组最简单的是变长一维数组,你可以这样做:  
       
      //文件名:   array01.cpp  
      #include<iostream>  
      using   namespace   std;  
       
      int   main()  
      {  
        int   len;  
        cin>>len;  
        //用指针p指向new动态分配的长度为len*sizeof(int)的内存空间  
        int   *p=new   int[len];  
        ...........  
        delete[]   p;  
        return   0;  
      }  
       
      注意int   *p=new   int[len];这一句,你不能这样做:  
      int   p[len];  
      C++编译器会报错说len的大小不能确定,因为用这种形式声明数组,数组的大小需要在编译时确定。而且这样也不行:  
      int   p[]=new   int[len];  
      编译器会说不能把int*型转化为int[]型,因为用new开辟了一段内存空间后会返回这段内存的首地址,所以要把这个地址赋给一个指针,所以要用int   *p=new   int[len];  
       
      array01.cpp实现了一个变长的一维数组,但是要养成一个好习惯,就是注意要注销指针p,使程序释放用new开辟的内存空间。  
      当然使用C++标准模版库(STL)中的vector(向量)也可以实现变长数组:  
       
      //文件名:   array02.cpp  
      #include<iostream>  
      #include<vector>  
      using   namespace   std;  
       
      int   main()  
      {  
        int   len;  
        cin>>len;  
        vector<int>   array(len);//声明变长数组  
       
        for(int   i=0;i<len;i++)  
        {  
          array[i]=i;  
          cout<<array[i]<<"\t";  
        }  
        return   0;  
      }  
       
      这里的变长数组让我联想到了java的java.util包中的vector和C#中的ArrayList,它们也可以在各自的语言中实现变长数组。不过C++中的vector不能像C#一样有托管的垃圾回收机制回收被占用的内存空间,但是你可以在使用完vector后调用~vector()析构函数释放内存。  
       
      2.变长n维数组  
      变长的n维数组实现起来有些麻烦,但是在工程与软件设计应用中常使用的是二维数组,所以在这里着重介绍变长的二维数组,变长的n维数组可以按照类似的方法实现。首先看一个经典的用C实现变长二维数组的例子:  
       
      //文件名:   array03.c  
      #include     <stdio.h>      
      #include     <malloc.h>      
         
      void     main()      
                                 
      {      
                            int     x,y,i,j;      
                            float     **a,*b;      
                                                    printf("请输入你所求解的线性方程组的行数x:x=");      
                            scanf("%d",&x);      
                                                    printf("请输入你所求解的线性方程组的列数y:y=");      
                            scanf("%d",&y);      
         
                    a=(float     **)malloc(sizeof(float     *)     *x);      
                    b=(float     *)malloc(sizeof(float)     *x);      
                            for(i=0;i<x;i++)      
                            {      
                                                    *(a+i)=(float     *)malloc(sizeof(float)     *y);      
                            }      
         
      /*读入数据*/      
         
                            printf("请按行的顺序依次输入系数的值(共%d项):",x*y);      
                            for(i=0;i<=x-1;i++)      
                                                    for(j=0;j<=y-1;j++)      
                                                                            scanf("%f",&a[i][j]);      
                            printf("请按列的顺序依次输入常数的值(共%d项):",x);      
                            for(j=0;j<=x-1;j++)      
                                                                            scanf("%f",&b[j]);      
         
                            printf("您输入方程组的增广矩阵为:\n");      
                            for(i=0;i<=x-1;i++)      
                            {      
                                                    for(j=0;j<=y-1;j++)      
                                                                            printf("%.5f         ",a[i][j]);      
                                                    printf("%.5f         ",b[i]);      
                                                    printf("\n");      
                            }      
                            free(b);      
                            for(i=0;i<x;i++)      
                                                    free     (*(a+i));    
      }  
       
      那么用C++怎样实现呢?在C++中可以通过new和delete运算符动态开辟和释放空间,其中new与C中malloc函数的功能相似,delete与C中free函数的功能相似。用C++实现变长二维数组时可以采用两种方法:双指针方法和使用STL中vector(向量)的方法。  
       
      首先介绍一下双指针方法,在这里双指针就是指像指针的指针,比如你可以这样声明一个数组:  
      int   **p   =   new   int*[num1];  
      而对每一个*p(一共num1个*p)申请一组内存空间:  
      for(int   i=0;   i<num1;   ++i)  
        p[i]   =   new   int[num2];  
      其中,num1是行数,num2是数组的列数。测试的源程序如下:  
       
      //文件名:   array04.cpp  
      #include   <iostream>  
      #include   <iomanip>  
      using   namespace   std;  
       
      int   main()  
      {  
        int   num1,//行数  
                num2;//列数  
       
        cout<<"Please   enter   the   number   for   row   and   column:   "<<endl;  
        cin   >>   num1   >>   num2;  
       
        //为二维数组开辟空间  
        int   **p   =   new   int*[num1];  
        for(int   i=0;   i<num1;   ++i)  
          p[i]   =   new   int[num2];  
       
        for(int   j=0;j<num1;j++)  
        {  
          for(int   k=0;k<num2;k++)  
          {  
            p[j][k]=(j+1)*(k+1);  
            cout<<setw(6)<<p[j][k]<<':'<<setw(8)<<&p[j][k];  
          }  
          cout<<endl;  
        }  
       
        //释放二维数组占用的空间  
        for(int   m=0;m<num1;m++)  
          delete[]   p[m];  
        delete[]   p;  
       
        return   0;  
      }  
       
      以下是运行结果:  
       
      Please   enter   the   number   for   row   and   column:  
      4   5  
                1:004915F0           2:004915F4           3:004915F8           4:004915FC           5:00491600  
                2:00491180           4:00491184           6:00491188           8:0049118C         10:00491190  
                3:00491140           6:00491144           9:00491148         12:0049114C         15:00491150  
                4:00491100           8:00491104         12:00491108         16:0049110C         20:00491110  
      Press   any   key   to   continue  
       
      程序清单array04.cpp可以显示分配的内存空间单元的地址,大家可以看到,由于数组空间是动态分配的,数组行之间的地址空间是不连续的,因为不同行的数组元素的地址空间是用不同的new来分配的。而每一行之中列之间的地址空间是连续的。  
       
      那么用vector(向量)怎样实现二维数组呢?以下给出源程序:  
       
      //文件名:   array05.cpp  
      #include   <iostream>  
      #include   <vector>  
      #include   <iomanip>  
      using   namespace   std;  
      int   main()  
      {  
        int   i,  
                j,  
                m,   //行数  
                n;   //列数  
       
        cout   <<   "input   value   for   m,n:";  
        cin>>m>>n;  
         
        //注意下面这一行:vector<int后两个">"之间要有空格!否则会被认为是重载">>"。  
        vector<vector<int>   >   vecInt(m,   vector<int>(n));      
        for   (i   =   0;   i   <   m;   i++)  
          for   (j   =   0;   j   <   n;   j++)  
            vecInt[i][j]   =   i*j;    
             
        for   (i   =   0;   i   <   m;   i++)  
        {  
          for   (j   =   0;   j   <   n;   j++)  
            cout<<setw(5)<<vecInt[i][j]<<":"<<setw(9)<<&vecInt[i][j];  
          cout<<endl;  
        }      
        return   0;  
      }  
       
      以下是运行结果:  
       
      input   value   for   m,n:3   4  
              0:   00491180         0:   00491184         0:   00491188         0:   0049118C  
              0:   00491140         1:   00491144         2:   00491148         3:   0049114C  
              0:   00491100         2:   00491104         4:   00491108         6:   0049110C  
      Press   any   key   to   continue  
       
      大家可以看到,这里vector中元素的内存的地址分配也有同双指针实现的二维数组有同样的特点。不过用vector的方法比使用双指针简单地多,分配内存空间时会更安全,数组初始化代码也更简单,所以本人建议使用STL中的vector来实现变长多维数组。以下是一个变长三维数组:)  
       
      //文件名:   array06.cpp  
      #include   <iostream>  
      #include   <vector>  
      #include   <iomanip>  
      using   namespace   std;  
      int   main()  
      {  
        int   i,  
          j,  
          k,  
          m,   //一维坐标  
          n,   //二维坐标  
          l;   //三维坐标          
       
        cout   <<   "input   value   for   m,n,l:";  
        cin>>m>>n>>l;  
        vector<vector<vector<int>   >   >   vecInt(m,   vector<vector<int>   >(n,   vector<int>(l)));      
        for   (i   =   0;   i   <   m;   i++)  
          for   (j   =   0;   j   <   n;   j++)  
            for(k   =   0;   k   <   l;   k++)  
              vecInt[i][j][k]   =   i+j+k;    
             
        for   (i   =   0;   i   <   m;   i++)  
        {  
          for   (j   =   0;   j   <   n;   j++)  
          {  
            for(k   =   0;   k<l;   k++)  
              cout<<setw(5)<<vecInt[i][j][k]<<":"<<setw(9)<<&vecInt[i][j][k];  
            cout<<endl;  
          }  
          cout<<endl;  
        }  
       
        return   0;  
      }  
       
      运行结果:  
      input   value   for   m,n,l:2   3   4  
              0:   00492FE0         1:   00492FE4         2:   00492FE8         3:   00492FEC  
              1:   00492FA0         2:   00492FA4         3:   00492FA8         4:   00492FAC  
              2:   00492F60         3:   00492F64         4:   00492F68         5:   00492F6C  
       
              1:   00492EC0         2:   00492EC4         3:   00492EC8         4:   00492ECC  
              2:   00492E80         3:   00492E84         4:   00492E88         5:   00492E8C  
              3:   00492E40         4:   00492E44         5:   00492E48         6:   00492E4C

               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • 数组引用:C++ 数组做参数 深入分析

    千次阅读 2018-06-07 20:59:58
    转载:https://blog.csdn.net/jiangxinyu/article/details/7767065在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指针... 数组的长度与参数声明无关,因此,下列三个声明是等价的: /...

    转载:https://blog.csdn.net/jiangxinyu/article/details/7767065


    在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指针

    例如,如下声明 :
    void putValues( int[ 10 ] ); 
    被编译器视为  
    void putValues( int* ); 
    数组的长度与参数声明无关,因此,下列三个声明是等价的: 
    // 三个等价的 putValues()声明 
    void putValues( int* ); 
    void putValues( int[] ); 
    void putValues( int[ 10 ] ); 

     

    因为数组被传递为指针 所以这对程序员有两个含义:


    1. 在被调函数内对参数数组的改变将被应用到数组实参上而不是本地拷贝上,当用作实参的数组必须保持不变时,程序员需要保留原始数组的拷贝函数可以通过把参数类型声明为 const 来表明不希望改变数组元素
    void putValues( const int[ 10 ] ); 

    2.  数组长度不是参数类型的一部分,函数不知道传递给它的数组的实际长度,编泽器也不知道,当编译器对实参类型进行参数类型检查时,并不检查数组的长度。例如:
    void putValues( int[ 10 ] ); // 视为 int* 

    int main() { 
    int i, j[ 2 ]; 
    putValues( &i ); // ok: &i 是 int*; 潜在的运行错误 
    putValues( j ); // ok: j 被转换成第 0 个元素的指针 
    // 实参类型为 int*: 潜在的运行错误 
    return 0; 

    参数的类型检查只能保证putValues()的两次调用都提供了int*型的实参,类型检查不能检验实参是一个 10元素的数组 。习惯上, C风格字符串是字符的数组,它用一个空字符编码作为结尾。但是所有其他类型,包括希望处理内含空字符的字符数组必须以某种方式在向函数传递实参时使其知道它的长度。

    一种常见的机制是提供一个含有数组长度的额外参数。例如:
    void putValues( int[], int size );
    int main() { 
           int i, j[ 2 ]; 
           putValues( &i, 1 ); 
           putValues( j, 2 ); 
           return 0; 
    }

    另外一种机制是将参数声明为数组的引用

    当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。 
    // 参数为 10 个 int 的数组 
    // parameter is a reference to an array of 10 ints 
    void putValues( int (&arr)[10] );//不能写成&arr[10],因为下标操作符的优先级较高
    int main() { 
           int i, j[ 2 ]; 
           putValues( i ); // 错误: 实参不是 10 个 int 的数组 
           putValues( j ); // 错误: 实参不是 10 个 int 的数组 
           return 0; 
    }

     

    展开全文
  • 数组做参数,值传递和引用传递

    千次阅读 2020-06-09 11:10:45
    #include <iostream> using namespace std;...void myprint(T * arr,int len)//数组做参数引用传递传参 { for (int i = 0; i < len; i++) { cout << arr[i] << endl; } } temp
    #include <iostream>
    using namespace std;
    #include <string>//(1)只要涉及string打印输出要加此文件!!!
    
    template<typename T>
    void myprint(T * arr,int len)//(3)数组做参数,引用传递传参
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << arr[i] << endl;//(2)注意:数组做函数参数---无论是值传递还是引用传递,函数里边对数组的操作 写法形式 都一样!!!都是arr[i]!!!
    	}
    }
    template<typename T>
    void myprint1(T arr[], int len)//(3)数组做参数,且值传递传参
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << arr[i] << endl;//(2)注意:数组做函数参数---无论是值传递还是引用传递,函数里边对数组的操作 写法形式 都一样!!!都是arr[i]!!!
    	}
    }
    int main()
    {
    	char Array[] = "defabc";
    	int len = sizeof(Array) / sizeof(Array[0]);
    	//myprint(arr1[6], len);//(1)错误!数组作为函数参数时,无论是值传递还是引用传递,都是直接单单写一个数组名!!!!!!!!!!
    	myprint(Array len);
    	//myprint1(arr1[6], len);//(1)错误!数组作为函数参数时,无论是值传递还是引用传递,都是直接单单写一个数组名!!!!!!!!!
    	myprint1(Array, len);
    	system("pause");
    	return 0;
    }
    

    //注意:(1)数组作为函数参数时,无论!是值传递还是引用传递,都是直接单单写一个数组名!!!
    (2)注意:数组做函数参数—无论是值传递还是引用传递,函数里边对数组的操作 写法形式 都一样!!!都是arr[i]!!!—就是数组该怎么操作怎么操作!(本质区别只是引用传递操作了实参本身,值传递只是在操作实参的一个副本!)
    (3)注意数组做参数是,形参的写法------
    a.引用传递: 数据类型 * arr
    b.值传递 : 数据类型 arr[ ]
    (4)函数参数引用传递形式:
    a.对于函数定义void myprint(T * arr,int len)—就是类型后面带上星花,如T *
    b.对于函数调用:
    如果是数组-----直接单单只写一个数组名!(因为数组名本身就是地址)
    如果是其他-----就要写成&A。取地址!

    展开全文
  • 数组做参数的方法

    2015-05-18 16:03:53
    c++中,数组做参数,会自动退化为普通指针。 如何避免这个问题呢,我想到的,两个方式。 1,使用数组引用,比如void fun(int (&arr)[10])(注意操作符优先级); 2,传入个数组长度的参数控制对指针边界。...

    c++中,数组做参数,会自动退化为普通指针。

    如何避免这个问题呢,我想到的,两个方式。

    1,使用数组的引用,比如void fun(int (&arr)[10])(注意操作符优先级);

    2,传入个数组长度的参数控制对指针边界。如void fun(int argc,char **argv)

    展开全文
  • 引用数组参数的传递

    2008-11-24 23:18:00
    如果有 testRef (int (&a)[10])这样的函数声明那么传递进来的参数就一定是int [10]的,要检查个数。中括号的优先级比较高所以要加括号。如果没有就是引用数组了。
  • C#中数组做参数问题

    千次阅读 2013-09-24 20:25:09
    引用类型:存储于 托管堆中,传递的是“引用(地址)”,如:类、OBJECT、字符串、数组  ref 关键字使参数引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数的任何更改都将反映在该变量中。 ...
  • 主要给大家介绍了关于C#中参数数组引用参数和输出参数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 数组也是引用型数据,把数组作为参数传递时,也是引用传参。 数组作为参数传递的两种形式: (1)在形参数组前不添加params修饰符,所对应的实参必须是一个数组名。 (2)在形参数组前添加params修饰符,所对应...
  • 一、大概区别 就像指针的数组和数组的指针一样耐人寻味 ...//array首先和&结合,所以array是引用引用的对象是数组 //应该叫作数组引用 int (&array)[10] 二、引用数组 首先,可以明确的说明,
  • 实际上,这三种形式是等价的,在使用数组做参数时,编译器会自动将数组名转换为指向数组第一个元素的指针,为什么呢?这要从参数的传递方式说起,参数有三种传递方式,按值传递,按指针传递,按引用传递,分别如下 ...
  • 2、二维数组做参数 在一维中我们看到,遍历数组时必须有终止条件,可以是某种标志也可以规定移动次数。 当到二维的时候这种情况就更为复杂。很多人定义函数的时候将函数定义为双重指针,传参的时候直接...
  • 1 数组作为参数 我们可以将数组作为参数,传入到函数中,其实就像我们main函数中 public void main(String [] args){};就是用数组作为函数参数; 又如, [java] view plain copy public class ...
  • 复制数组而不是引用数组 在js中,对象(object)和数组(array)类型的变量为引用类型,也就是说其存储的实际上并不是具体的值,而是一个指向堆内存的地址。 因此当把一个引用类型,如‘数组’,赋值给另一个变量时...
  • 引用变量和引用数组

    千次阅读 2013-01-30 10:58:20
    其实引用和指针有很多相似的地方,又有不同的(太多了,不过说到效率上,比如函数传参数,我们可以用引用,指针,哪种好呢,引用不必为站再分配空间了,而指针还学要分配4字节的空间给指针变量) 我们知道如何引用...
  • C++数组作为参数

    千次阅读 2018-07-23 20:59:45
    C++里面把数组参数这个知识点一直不是很清晰。这次把他明确化了。 非引用参数引用参数的情况还是比较简单的。 int compute(int array[4]); int compute(int array[]); int compute(int *array); // 编译器会...
  • c++中数组作为参数传入函数

    万次阅读 多人点赞 2019-04-15 19:23:38
    其中涉及了大量的数组运算,所以数组作为参数传入函数很重要,这里记录一下。 一维数组作为参数 1.我们除了要将数组名传入函数外,为了规范化,我们还要将数组的大小也要作为参数传入,而不是在函数内部去定义大小...
  • c++ 二维数组作为参数引用

    千次阅读 2018-05-12 09:35:17
    //左相机内参数矩阵 float leftIntrinsic[3][3]; //左相机旋转参数 float leftRotation[3][3]; //左相机平移向量 float leftTranslation[1][3]; //右相机内参数矩阵 float rightIntrinsic[3][3]; //右相机...
  • golang-数组做函数参数

    千次阅读 2019-09-08 13:38:55
    2)数组作为函数参数,收到的是数组的一个拷贝,而不是它的指针 3)数组的大小是类型的一部分,[10]int和[20]int是不一样的 这里重点探索一下 2) 首先看一下C++的代码: #include <iostream> #include<map...
  • 关于数组的引用和引用数组

    千次阅读 2014-04-03 14:14:48
    一、大概区别 就像指针的数组和数组的指针一样...//array首先向右结合,所以这个相当于 (int&)array[] array是个数组,其中的元素是引用 //应该叫:引用数组 int &array[] //array首先和&结合,所以arr
  • c++ 数组作为参数、返回值

    千次阅读 2020-06-27 10:32:37
    数组作为参数 写C++代码遇到数组作为参数,常常忘记怎么写形参,这里记录一下。 一维数组作为参数 传入数组 int [] 为了规范,常常需要将数组的size一同传入,这是因为C++/C遇到参数为数组时,不会去一个个拷贝...
  • 基本数据类型参数传递 数组类型参数传递 引用数据类型参数传递
  • 并且,这表明C语言的数组作为参数传递给函数时,是作为引用方式传递的。 还有,在传递的时候,还需要把数组的大小也一并传递给函数。因为只传递数组给函数,进而想要在函数中获得数组大小,在网上搜了一下,答案居然...
  • 使用对象数组作为参数,只是将对象作为一个数组元素,其引用方法与基本数据类型的数组完全相同。但针对对象数组中的单个元素来讲,它又具有对象有属性和方法。 import java.util.Scanner; //学生类class Student{ ...
  • https://blog.csdn.net/qq_30600259/article/details/101551220
  • 数组做参数

    2008-06-26 21:21:00
    下面列出以数组做参数的例子来对比一下区别:1.void putValues( int[ 10 ] ); //相当于传递 int *,数组的长度将不检测等价的表示如下: void putValues( int* ); void putValues( int[] );2.传递数组的指针及数组...
  • void swap(int *a,int*b){ *a=*a^*b; *b=*a^*b; *a=*a^*b;...以上是我遇到的问题,我觉得调用这个swap函数是不能这样直接把数组的某个元素直接丢给swap数据在程序中参加数据处理的量不是指针本身的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 586,493
精华内容 234,597
关键字:

引用数组做参数