C/C++ 数组作为参数传递 指针 引用——VS——普通值传递、指针传递、引用传递

27 篇文章 2 订阅

1 C++ 值传递、指针传递、引用传递详解

值传递:

形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。

指针传递:

形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作

引用传递:

  • 形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作。
  • 在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。
  • 被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。
  • 正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

下面的代码对此作出细致解释(从实参,形参在内存中存放地址的角度 说明了问题的本质,容易理解 )

#include <iostream>
#include <vector>

using namespace std;

#include<iostream>
using namespace std;
//值传递
void change1(int n){
    cout<<"值传递--函数操作地址"<<&n<<endl;         //显示的是拷贝的地址而不是源地址
    n++;
}

//引用传递
void change2(int & n){
    cout<<"引用传递--函数操作地址"<<&n<<endl;
    n++;
}
//指针传递
void change3(int *n){
    cout<<"指针传递--函数操作地址 "<<n<<endl;
    *n=*n+1;
}
int main(){
    int n=10;
    cout<<"实参的地址"<<&n<<endl;
    change1(n);
    cout<<"after change1() n="<<n<<endl;
    change2(n);
    cout<<"after change2() n="<<n<<endl;
    change3(&n);
    cout<<"after change3() n="<<n<<endl;
    return true;
}

在这里插入图片描述
引用的规则:

(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

参考链接:https://www.cnblogs.com/yanlingyin/archive/2011/12/07/2278961.html

2 数组作为函数的形参

2.1 一维数组传递

然而,由于在C和C++中数组不能直接复制,传递时只传递一个首地址,在函数中需要使用的时候再跟据首地址和下标去寻找对应的值。
至于为何数组不能直接复制,有一种解释的说法:

  • 是为了避免不必要的复制开销,因为数组的复制将导致连续的内存读与内存写,其时间开销取决于数组长度,有可能会变得非常大。为了避免复制数组的开销,才用指针代替数组。因此C语言使得当数组作为实参传递给函数的时候,将退化为同类型的指针,再传递指针的值。
    因此,在函数中修改数组值时,修改的是真的值。
#include <iostream>
#include <vector>

using namespace std;

#include<iostream>
using namespace std;
//值传递
  void printdz(int a[])
  {
  	cout<<"子函数里a0的地址为"<<&a[0]<<endl;
  	cout<<"子函数里a的地址为"<<a<<endl; //a本身即为指针 无需取地址符
  	cout<<"子函数里a1的首地址为"<<&a[1]<<endl;
  	a[1]=2;
  }
  int main()
  {
    int a[10]={1};
    cout<<"主函数里a的首地址为"<<&a[0]<<endl;
  	cout<<"开始的a1为 "<<a[1]<<endl;
  	printdz(a);
  	cout<<"函数里改过的a1 为"<<a[1]<<endl;
  	return 0;
  }

在这里插入图片描述

  • 在这里我们可以看到子函数里的首地址a[0]与主函数的首地址是相同的。
  • a已经是一个指针了。由于是通过指针传递,因此无法得到数组的长度

除了退化为指针传递,还可以直接通过指针传递和通过引用传递。

指针传递:

void printdzyy(int *aa) //传入指针 or void printdzyy(int aa[])
{
	cout<<"函数里参数的地址为"<<&aa[0]<<endl;
}
int main()
{
  int a[10]={1};
	int b[5]={1};
  cout<<"主函数里a[0]的首地址为"<<&a[0]<<endl;
	printdzyy(a);
  printdzyy(b);//传入指针时 编译器无法知道数组长度 因此可以随便传
	return 0;
}

在这里插入图片描述

通过引用传递::

void printdzyy(int (&aa)[10]) //引用就可以传递数组长度 因此需要写出数组大小
{
	cout<<"函数里aa的地址为"<<aa<<endl;
}
int main()
{
  int a[10]={1};
	int b[5]={1};
  cout<<"主函数里a[0]的首地址为"<<&a[0]<<endl;
	printdzyy(a);
  //printdzyy(b);  incorrect  //如果传入b则编译不通过
	return 0;
}

在这里插入图片描述

  • 从上述程序可以知道,传入指针时,编译器无法知道数组长度,因此可以传递进去不同长度的数组,也可以不写出数组长度。
  • 但是传递引用时,同时将数组长度也传递进去了,所以传入时必须为相应长度的数组。并且,在传递引用时,需要注意用()将&aa括起来,否则编译器报错。
  • 对于引用,也叫“别名”,某种意义上可作为无地址的指针,相比指针,引用不存在地址,必须被初始化,不存在NULL,不可改变对象,引用无法被多重引用。因此更加安全

2.2 二维数组传递

可以理解为数组的定义等同于指针的定义,即*a等同于a[],因此可以作如下变换

#include <iostream>
#include <vector>

using namespace std;

//1.5
void func1(int iArray[][10])  //等同于 void func1(int (*iArray)[10])
{
}  //等同于void func1(int iArray[10][10])
int main()
{
    int array[10][10];
    func1(array);
}

注意(iAarray)可通过,去掉括号会编译错误*

由于二维数组在栈内分配的内存是连续的,需要告诉编译器偏移的点,所以第二维度不可省,必须符合,而第一维度如一维数组一样随意。因此在上述代码1.5 中void func1(int iArray[9][10])不会报错,而void func1(int iArray[10][9])会报错。

同一维数组,这边推荐使用引用,使用引用时需要保证两个维度都要ok:

//1.6
void func3(int (&pArray)[10][10])  
{  }  
int main()  
{  
    int array[10][10];  
    func3(array);  
}  

也可以指针的指针来表示二维数组,动态分配内存的形式,此时,严格来说,并不是二维数组。

#include <iostream>
#include <stdio.h>
void out(double **a,int m, int n)
{
    int i, j;
    double b=0.0;
    for(i=0; i<m; i++)
    {for (j=0; j<n; j++)
        {
            a[i][j] = b;
            b += 1.2;
            printf("%5.1f",a[i][j]);
        }
        std::cout << std::endl; }   
}
int main(int argc, char * agrv)
{
    int i, j, m=2, n=3;
    double **a;
    a = new double*[m];
    for (i=0; i<m; i++)
        a[i] = new double[n];
    out(a,m,n);
    return 1;
}

总结

在传递数组参数时,首要推荐通过引用来传递参数,精准传入数组大小值,函数中参数定义为
vtype (&name)[size][size],引用时传入名字即可。

在通过指针传递时,需要另外传入参数来传递数组的大小。

参考:
https://blog.csdn.net/qq_30600259/article/details/101551220
https://blog.csdn.net/desilting/article/details/7983530

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值