• 数组类型形参


    void f(int p*)

    void f(int p[])

    void f(int p[4])

    对于传递数组类型的参数,一直很晕,其实以上三者是相同的,c语言中不能传递数组,只能传递第一个元素的地址,所以c语言在函数中没有数据越界方面的保护机制,所以可以额外的传递一个表示大小的参数,还可以这样void f(int (&p)[12]),这个p表示指向数组的指针,数组大小是12,我是这样理解的。

    数组有两个特殊的性质。一是不能复制数组;二是使用数组名字时,数组会自动转化为指向其第一个元素的指针。由于数组不能复制,所以无法编写使用数组类型形参的函数,因为数组会被自动转化为指针。
    
       数组形参是个容易出问题的地方。因为对于C/C++新手而言,最大的惊讶是C++中根本不存在所谓的“数组形参”,因为数组在传入时,实质上只传入指向其首元素的指针。
    
    
    1 void average(int ary[12]); // 形参ary是一个int*
    2 
    3 //...
    4 
    5 int anArray[] = {1, 2, 3}; // 一个具有3个元素的数组
    6 
    7 const int anArraySize = sizeof(anArray)/sizeof(anArray[0]); // == 3
    8 
    9 average(anArray);//error
    
     
    
    这种从数组到指针的自动转换被赋予了一个迷人的技术术语:“退化”,即数组退化成指向其首元素的指针。顺便提及,同样的事情也发生在函数上。一个函数型参数会退化成一个指针。不过,和数组退化时丢失边界不同,一个退化的函数具有良好的感知能力,可以保持其参数类型和返回类型。
     
    
        由于在数组形参中数组边界被忽略了,因此通常在声明时最好将其省略。然而,如果函数期望接受一个指向一个元素序列(换句话说,就是数组)的指针作为参数,而不是指向单个对象的指针,那么最好这样声明:
    void average(int ary[]); // 形参ary仍然是一个int*
    另一方面,如果数组边界的精确数值非常重要,并且希望函数只接受含有特定数量元素的数组,可以考虑使用一个引用形参:
     
    
     void average(int (&ary)[12]);
    // ...
    int main()
    {
    int i=0,j[2]={0,1};
    int k[12]={0,1,2,3,4,5,6,7,8,9,10,11};
    average(&i);  //error: 参数类型不是10个int型
    average(j);    //error: 
    average(k);  //ok;
    return -1;
    }
    
    现在,由于average需要接受的参数为12个元素的数组,而anArray是一个只含有3个元素的数组,因此:
    
     average(anArray); // 错误!anArray是一个int [3] !
    参考资料:http://blog.chinaunix.net/space.php?uid=10314004&do=blog&id=2964108
     
     
        模板有助于代码的泛化:
        template<int n>
        void average(int (&ary)[n]); // 让编译器帮我们推导n的值!
     
        不过,更为传统的做法是将数组的大小明确地传入函数:
        void average(int ary[], int size);
     
        当然,我们可以将这两种方式结合起来:
        template<int n>
        inline void average(int (&ary)[n])
        {
             average_n(ary, n);
        }
     
        从以上讨论中我们应该清晰地获知,使用数组合作函数参数最大的问题在于,数组的大小必须以形参的方式显式地编码,并以单独的实参传入,或者在数组内部以一个结束符值作为指示。另一个困难在于,不管数组是如何声明的,一个数组通常是通过指向其首元素的指针进行操纵。如果那个指针作为实参传递给函数,我们前面声明引用形参的技巧将无济于事。
    int *anArray2 = new int [anArraySize];
    //...
    average(anArray2); //错误,不可以使用int *初始化int (&)[n]
    average_n(anArray, anArraySize); // 没问题
     
        出于这些原因,经常采用某种标准窗器(通常是vector或string)来代替对数组的大多数传统的用法,并且经常应该优先考虑使用标准容器。
        从本质上来说,多维数组形参并不比一维数组来得困难,但它们看上去更具挑战性;
    void process(int ary[10][20]);
        和一维数组一样,形参不是一个数组,而是一个指向数组首元素的指针。不过,多维数组是数组的数组,因此形参是一个指向数组的指针:
    void process(int (*ary)[20]); //一个指针,指向一个具有20个int元素的数组
     
        注意,第二个边界没有退化,否则将无法对形参执行指针算术。如前所述,让代码的读者清晰地知道你期望的实参是一个数组,这通常是一个好主意:
    void process(int ary[][20]); // 仍然是一个指针,但更清晰
     
        对多维数组形参的有效处理往往退化成一个低级的编程练习,此时需要程序员取代编译器执行索引计算。
       
    void process_2d(int *a, int n, int m)
    {
          for (int i = 0; j < n; ++i)
                for (int j = 0; j < m; ++j)
                      a[i*m+j] = 0;
    }
        同样,有时模板有助于让事情更干净利落:
     
    template<int n, int m>
    inline void process(int (&ary)[n][m])
    {
          process_2d(&ary[0][0], n, m);
    }
     
        一句话数组形参是个讨厌的家伙,和它亲近你得小心。
  • 相关阅读:
    一文带你快速认识“华为3D内容平台”!
    [论文解读] 阿里DIEN整体代码结构
    [论文阅读]阿里DIEN深度兴趣进化网络之总体解读
    [阿里DIN] 从论文源码学习 之 embedding层如何自动更新
    [阿里DIN]从论文源码学习 之 embedding_lookup
    [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构
    [阿里DIN] 深度兴趣网络源码分析 之 如何建模用户序列
    aspell命令
    买卖股票的最佳时机含手续费
    grep命令
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/3619360.html
Copyright © 2020-2023  润新知