精华内容
下载资源
问答
  • 数组形参

    千次阅读 2016-01-18 19:10:57
    数组有两个特殊的性质。一是不能复制数组;...因为对于C/C++新手而言,最大的惊讶是C++中根本不存在所谓的“数组形参”,因为数组在传入时,实质上只传入指向其首元素的指针。 1 void average(in

      数组有两个特殊的性质。一是不能复制数组;二是使用数组名字时,数组会自动转化为指向其第一个元素的指针。由于数组不能复制,所以无法编写使用数组类型形参的函数,因为数组会被自动转化为指针。

       数组形参是个容易出问题的地方。因为对于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] !
     
     
        模板有助于代码的泛化:
        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);
    }

     

        一句话数组形参是个讨厌的家伙,和它亲近你得小心。

    展开全文
  • 数组形参 当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针 数组形参不能以值传递的方式传递数组 以下三种写法等价,每个函数唯一的形参都是const int * void print(const int *) void print(const ...

    数组形参
    当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针
    数组形参不能以值传递的方式传递数组

    以下三种写法等价,每个函数唯一的形参都是const int *
    void print(const int *)
    void print(const int[])
    void print(const int[10])
    

    当传入一个数组给print函数,实参会自动地转换为指向数组首元素的指针
    使用数组形参时,必须确保使用数组时不会越界
    管理指针形参,常用的3种技术

    使用标记指定数组长度

    void print(const char* cp) {
    	if (cp)//如果指针不为空
    		while (*cp)//如果指针所指对象不为空
    			cout << *cp++;//输出当前字符并将指针向后移动一个位置
    }
    int main(){
    	char c[] = { 'a','b','c','d' };
    	print(&c[0]);//传入数组的首元素指针 
    	print(begin(c));//这种传入方式也可以
    	//需要注意数组的尾后元素\0
    return 0;
    }
    

    使用标准库规范

    void print(const int* beg, const int* end) {
    	//输出beg到end(不包含end)之间的所有元素
    	while (beg != end) {
    		cout << *beg++ << endl;//输出当前元素并将指针向后移动一个位置
    	}
    }
    int main()
    {
    
    	int a[] = { 1,2,3 };
    	print(&a[0], &a[3]);//第一种调用方式
    	print(begin(a), end(a));//第二种调用方式
    	return 0;
    	}
    

    显示传入一个表示数组大小的形参

    void print(const int ia[], size_t size) {
    	//const int* ia 等价于 const int ia[]
    	for (size_t i = 0; i != size; ++i) {
    		cout << ia[i] << endl;
    	}
    }
    int main()
    {
    
    	int a[] = { 1,2,3 };
    	print(a,3);
    	//在不知道a的大小的情况下可以用以下两种方式
    	print(a, end(a)-begin(a));
    	print(a, sizeof(a)/sizeof(a[0]));
    	return 0;
    	}
    

    以上的例子都是不需要修改数组中的值,所以都用了指向const的指针,如果函数需要修改值就用非常量指针(就是不带const的普通指针)

    C++允许将变量定义成数组的引用,基于同样的道理形参也可以是数组的引用

    void print(int(&arr)[10]) {//引用数组作为形参一定是(&arr)[10]这种写法
    	for (auto i : arr) {
    		cout << i << endl;
    	}
    }
    int main(){
    
    	int x[10] = { 1,2,3,4,5,6,7,8,9,10 };
    	print(x);//这个函数只能传入10个大小的数组
    
    return 0;
    }
    
    需要注意,作为函数形参数组的引用
    &arr必须加()
    f(int &arr[10])//错误这是将arr声明成了引用的数组
    f(int (&arr)[10]//正确,arr是有10个整数的整数数组的引用
    

    传递多维数组
    C++没有真正的多维数组,多维数组只是数组的数组
    所以多维数组在传递时,传递的是数组的数组的首元素

    void print(int(*matrix)[10], int rowSize);//这里一定是(*matrix)[10]
    //等价与
    void print(int matrix[][10],int rowSize);
    
    int *matrix[10] //10个指针构成的数组
    
    

    练习6.21

    int func(const int &x,  const int *const y) {
    	return x > *y ? x : *y;
    }
    int mian(){
    	int a = 1;
    	int b = 2;
    	int* c = &b;
    	cout<<func(a, c)<<endl;
    	return 0;
    	}
    

    练习6.22

    void func2( int* x, int* y) {
    	int* temp = x;
    	x = y;
    	y = temp;
    }
    int main(){
    	int a = 0;
    	int *i = &a;
    	cout << *i << endl;
    	cout << i << endl;
    	int b = 1;
    	int* j = &b;
    	cout << *j << endl;
    	cout << j << endl;
    	func2(i, j);
    	cout << *i << endl;
    	cout << i << endl;
    	cout << *j << endl;
    	cout << j << endl;
    return 0;
    }
    

    练习 6.23

    #include<string>
    #include <iostream>
    #include<cctype>
    using namespace std;
    
    void print(const int ia[], size_t size) {
    	//const int* ia 等价于 const int ia[]
    	for (size_t i = 0; i != size; ++i) {
    		cout << ia[i] << endl;
    	}
    }
    void print(const int* beg, const int* end) {
    	//输出beg到end(不包含end)之间的所有元素
    	while (beg != end) {
    		cout << *beg++ << endl;//输出当前元素并将指针向后移动一个位置
    	}
    }
    void print(const char* cp) {
    	if (cp)//如果指针不为空
    		while (*cp)//如果指针所指对象不为空
    			cout << *cp++;//输出当前字符并将指针向后移动一个位置
    }
    
    void print(int* const p) {
    	cout << *p << endl;
    }
    int main()
    {
    
    	int i = 0, j[2] = { 0,1 };
    	char c[5] = { 'a','b','c','d','e' };
    	print(&i);
    	print(c);
    	print(begin(j),end(j));
    	print(j, end(j) - begin(j));
    	system("pause");
    	return 0;
    }
    

    练习6.24
    当我们传递一个数组给一个函数时,实际上是在传递一个指向数组第一个元素的指针。这题,参数中的const int ia[10]实际上和const int*是一样的,数组的大小是无关的,const int ia[3]或const int ia[255],没有区别。所以如果我们传入的实参数组是int ia[5],那么在该函数中的for循环里访问ia[5]时就会出错,数组越界。如果我们想要传递一个大小为10的数组,我们应该像这样使用引用:

    void print(const int (&ia)[10]) {
    	for (size_t i = 0; i != 10; ++i) {
    		cout << ia[i] << endl;
    	}
    }
    
    展开全文
  • 【C语言】数组形参

    2017-07-15 23:30:17
    数组形参

    1、数组可以作为形式参数使用

    2、数组做形式参数的时候真正的形式参数并不是数组而是一个可以作为数组使用的变量

    3、数组形式参数里包含的存储区都不是被调用函数提供的

    4、声明数组形式参数的时候可以省略中括号里的整数

    5、使用数组形式参数的时候必须配合一个整数类型的形式参数,这个参数用来表示数组里的存储区个数

    6、数组形式参数可以让被调用函数使用其他函数的存储区

    7、调用函数和被调用函数都可以自由使用数组形式参数里的存储区,所以数组形式参数可以实现双向数据传递,具有这种特征的形式参数叫输入输出参数

    /*
     * 数组形参演示
     * */
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    void print(int arr[],int size) { //定义一个数组的形参,arr[]方括号里的数字可以写可以不写,随意写都可以,因为这里的数组是之前早已创建好了的,不是被调函数提供的存储去,但是这里必须配合一个整数类型的参数来做大小
        int num = 0;
        for(num = 0; num <= size -1; num++) {
            printf("%d \n", arr[num]);
        }
    }
    
    void neg(int arr[], int size) {
        int num = 0;
        for(num = 0; num <= size -1; num++) {
            arr[num] = 0 - arr[num];
        } 
    }
    
    /*
     * 产生张彩票,用函数来实现,彩票张数由用户决定
     * */
    void cp(int p[],int num) {
        printf("num:%d\n",num);
        int cnt = 0, i = 0, c = 0;
    //    srand(time(0));
        do {
            c = rand() % 36 + 1;
         //   printf("c:%d", c);
            for(i = 0; i <= cnt - 1; i++) {
                if(p[i] == c) {
                    break;
                }
            }
    //        printf("p:%d cnt:%d \n", p[cnt], cnt);
            if(i == cnt) {
                p[cnt] = c;
                cnt++;
            }
        }while(cnt < num);
    }
    
    int add(int num, int num1) 
        return num + num1;
    }
    
    int main() {
     // int arr[] = {1, 2, 3, 4, 5};
     // print(arr,5);
     //   neg(arr,5);
    
    //   srand(time(0));//写在主函数中
    //   int num = 0;
    //   printf("请输入需要多少张彩票:\n");
    //   scanf("%d", &num);
    //   int p[num];
    //   cp(p,num);
    //   int i = 0;
    //   printf("产生的彩票是:\n");
    //   for(i = 0; i <= num - 1; i++) {
    //         printf("%d ", p[i]);
    //   }
    //   printf("\n");
        int num = 0; 
        printf("%d", add(3,8));
        return 0;
    }

    8、C语言里函数参数的个数可以不固定,这种参数叫做变长参数

    9、不能在编写函数的时候给变长参数命名,在被调用函数里可以通过特殊的方法获得没有命名参数的内容

    10、如果编译器首先遇到函数调用语句就会猜测函数的格式。计算机认为函数有一个整数类型的存储区用来存放返回值,函数可以接受任意多个不确定类型的参数。

    11、这个猜测结果叫做函数的隐式声明

    12、隐式声明里参数的类型只能是整数类型或双精度浮点类型

    13、如果函数的真实格式和隐式声明格式不同则编译的时候就会出错

    14、可以把函数大括号前面的部分单独写成一条语句,这种语句叫做函数声明语句

    15、把函数声明语句放在文件开头叫做显式函数声明

    16、函数的显示声明可以避免隐式声明的发生

    /*
     * 函数声名
     * */
    #include <stdio.h>
    int add(int, int); // 可以省略括号里面的行参名字;这个语句的作用就是让程序知道函数是什么类型的,知道类型后才可以创建存储区
    
    int main() {
        printf("num+num1=%d\n", add(3, 9));
        return 0;
    }
    
    int add(int num, int num1) {
        return num + num1;    
    }

    17、函数声明语句里可以省略形式参数的名称

    18、除了主函数以外的所有函数都应该显示声明

    19、exit标准函数可以立刻结束程序的执行,为了使用这个标准函数需要包含stdlib.h头文件,这个函数需要一个整数类型的参数,这个参数的作用和主函数返回值的作用一样

    /*
    * 终止函数exit
    **/
    #include <stdio.h>
    #include <stdlib.h>
    void func() {
        printf("2\n");
        exit(0);  //会结束掉后面的程序,跟reture 0效果一样
        printf("3\n");
    }
    int main() {
        printf("1\n");
        func();
        printf("2\n");
        return 0;
    }

    20、C语言里函数可以调用自己,这种函数叫做递归函数

    21、如果一个问题可以拆分成多个小问题,至少一个小问题和原来的问题在本质上一样但是稍微简单一点。这种问题就适合采用递归函数解决。

    22、递归函数编写步骤

    • 1.编写语句解决分解后的每个小问题(这个时候要假设递归函数已经完成,可以使用)
      2.在递归函数开头编写分支解决不可分解的情况(这个分支必须可以让函数结束)

    23、使用递归函数解决问题的方法叫递归

    24、使用循环解决类似问题的方法叫递推

    24、检验递归函数的时候先用最简单的参数检验,然后逐渐把参数变复杂继续检验。如果所有参数的检验都能成功就说明递归函数编写正确。

    /*
     * 递归函数和递推函数
     * */
    #include <stdio.h>
    /*
     * 递归方法求阶乘
     * 编写的时候先把小一项的写出来,即jc_dg(num_jcdg-1)
     * 然后让本身和小一项相乘
     * 然后在上面将不可再化简的项写处,并列出最后的返回值
     * */
    int jc_dg(int num_jcdg) {
        if(num_jcdg == 1) {
            return 1;
        }
        return (jc_dg(num_jcdg - 1) * num_jcdg);
    }
    
    /*
     * 递推方法求阶乘
     * */
    int jc_dt(int num_jcdt) {
        int i = 0;
        int fac = 1;
        for(i = 1; i <= num_jcdt; i++) {
            fac *= i;
        }
        return fac;
    }
    /*
     * 递归实现汉诺塔
     * */
    
    int hnt(int n, char a, char b, char c) {
        if(n == 1)
             printf("\t%c->%c\n", a, c);
        else {
            hnt(n-1, a, c, b);
            printf("\t%c->%c\n", a, c);
            hnt(n-1, b, a, c);
        }
    }
    int main() {
    //    int num = 0;
    //    printf("请输入一个整数:\n");
    //    scanf("%d", &num);
    //    printf("jc_dg:%d的阶乘是:%d\n", num, jc_dg(num));
    //    printf("jc_dt:%d的阶乘是:%d\n", num, jc_dt(num));
    
        int n = 0;
       printf("请输入要移动的块数:\n");
        scanf("%d",&n);
        hnt(n,'a','b','c');
    }
    /*
     * 递归函数
     * */
     * 
    #include <stdio.h>
    void print() {
        printf("1\n");
        print();
    }
    
    void print_1(int num) {
        if(num == 1)
            printf("1");
        return; 
        print(num - 1);
        printf("num:", num);
    
    }
    /*
     * 用递归方法,输入一个数,然后累加
     * */
    int exercise_1(int num) {
       // static int sum;//用此方法必须注意加static
       // sum = sum + num;
        if(num == 1) {  //处理不可分解的事情
         //   sum = sum + num;  //不能写在里面,写在里面相当于前n-1次就没有进入这个运算,只最后一次算了一下
          return 1;
        }
        else {
            return (exercise_1(num-1) + num);
        }
    }
    
    int main() {
    //    print();
    //    print_1(10);
    
        int num = 0;
        printf("请输入一个整数:\n");
        scanf("%d", &num);
        printf("%d\n", exercise_1(num));
    
        return 0;
    }
    展开全文
  • C++数组形参

    2019-11-30 23:05:35
    C++数组形参 数组的两个特性: 1、数组不允许拷贝 2、使用数组时,通常会将其转换成指针 所以,我们无法以按值传递的方式传递数组,但仍然可以把形参写成类似数组的形式 void print(const int*); void print(const ...

    C++数组形参

    数组的两个特性:
    1、数组不允许拷贝
    2、使用数组时,通常会将其转换成指针
    所以,我们无法以按值传递的方式传递数组,但仍然可以把形参写成类似数组的形式

    void print(const int*);
    void print(const int[]);
    void print(const int[10]);
    int i = 0;
    int j[2] = {0, 1}
    print(&i); //正确,&i 的类型是int *
    print(j); // 正确,j转换成int *,并指向j[0]
    

    这三个函数是等价的:每个函数的唯一形参是const int *。

    展开全文
  • C++中指针和数组的学习,包括指针中常见问题,以及一维数组二维数组通过数组形参来进行函数传参,动态数组等问题
  • C++多维数组形参

    千次阅读 2016-05-13 22:14:30
    需求今天在学习数据结构和算法时遇到一个需要传递多维数组形参的问题。问题本身很简单,向方法传递一个二维数组,但是使用的时候为了能够让数组维度值能够动态可变(比如向同一个方法传递3X3矩阵或者4X4矩阵),也...
  • 17 C++基础-数组形参

    2018-04-13 19:46:49
    1.数组形参 数组特性 - 不允许拷贝数组 - 使用数组时通常会将其转换为指针 void print(const int*) { } void print(const int[]) { } void print(const int[10]) { } 上述的函数的数组形参是等价...
  • C++Primer 6.2节——参数传递 续数组形参 1、数组形参 ①数组的两个性质:不允许拷贝数组以及使用数组时通常会将其转换成指针。 ②当给函数传递一个数组时,实际上传递的是指向数组首元素的指针。 ③数组形参的定义...
  • //如果数组作为函数参数,数组形参退化为指针 //void print_array(int a[1], int n) //void print_array(int a[], int n) //如果数组作为函数参数,数组形参退化为指针 //void print_array(int a[1], int n) //void...
  • 数组引用形参和多维数组形参 void print(int (&arr)[10]) {..............} //如果形参里面是 (int &arr[10]) 这是引用的数组,相对于是包含10个引用的数组,这一般是非法的声明 这样的数组引用形参只能将...
  • 二维数组形参

    2014-10-15 23:30:06
    1.问题:写基数排序过程中发现需要进行二维数组形参(当然可以替换其他方式解决,比如用一位数组模拟桶),发现竟然编译不能通过,错误代码大致如下:
  • C++:数组形参的传递

    千次阅读 2019-01-15 09:59:57
    不允许拷贝:无法以值传递的方式使用数组形参 使用数组时通常都会转换为指针:当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。 传参的方法 当下有一个数组int j[2]={0,1},我们自写了一个函数 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,890
精华内容 2,756
关键字:

数组形参