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 C++ 值传递、指针传递、引用传递详解值传递:指针传递:引用传递:2 数组作为函数的形参2.1 一维数组传递2.2 二维数组传递总结1 C++ 值传递、指针传递、引用传递详解值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参-&gt;形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。指针传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作   声明一个Employee类,其中包括表示姓名、街道地址、城市和邮编等属性,包括ChangeName()和display()等函数。Display()使用cout语句显示姓名、地址、城市和邮编等属性,函数setName()改变对象的姓名属性,实现并测试这个类。 一个文件时: #include <iostream> #include <string> using...
值传递: 形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入, 不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递指针传递: 形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作 引用传递: 形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递...
十秒学会vs2010创建C语言文件,转载b站链接功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 功能快捷键 撤销:Ctrl/Command + Z 重:Ctrl/Command + Y 加粗:Ctrl/Command + B
C++传递数组参数 将数组作为参数进行传递有两种传递方法,一种是function(int a[]); 另一种是function(int *a)。这两种两种方法在函数中对数组参数的修改都会影响到实参本身的值! 对于第一种,根据之前所学,形参是实参的一份拷贝,是局部变量。但是数组是个例外,因为数组的数据太多了,将其一一赋值既麻烦又浪费空间,所以数组作为参数传递给函数的只是数组首元素的地址,数据还...
详解c++指针指针指针引用 以下参考自:https://www.cnblogs.com/li-peng/p/4116349.html 展示一下使用指针指针指针引用修改传递给方法的指针,以便更好的使用它。(这里说的指针指针不是一个二维数组) 为什么需要使用它们 当我们把一个指针为参数传一个方法时,其实是把指针的复本传递给了方法,也可以说传递指针指针值传递。 如果我们在方法内部修改...
一、数组名作为函数的参数,传递的是一个地址(或常量指针) #include<stdio.h> void arr(int a[])//数组名作为函数的参数,传递的是一个地址(或常量指针) int i; for(i=0;i<5;i++) printf("%d\
struct ListNode* arrayToList(int* nums, int size) { struct ListNode* head = NULL; struct ListNode* tail = NULL; for (int i = 0; i < size; i++) { struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); node->val = nums[i]; node->next = NULL; if (head == NULL) { head = node; tail = node; } else { tail->next = node; tail = node; return head; void printList(struct ListNode* head) { while (head != NULL) { printf("%d ", head->val); head = head->next; printf("\n"); int main() { int nums[] = {1, 2, 3, 4, 5}; int size = sizeof(nums) / sizeof(nums[0]); struct ListNode* head = arrayToList(nums, size); printList(head); return 0; 在上面的代码中,我们定义了一个结构体 `ListNode`,表示链表节点。然后定义了一个函数 `arrayToList`,用于将数组转换为链表。在函数中,我们遍历数组中的每个元素,创建一个新的节点,并将其插入到链表中。最后返回链表的头节点。我们还定义了一个函数 `printList`,用于打印链表中的所有元素。在 `main` 函数中,我们调用 `arrayToList` 函数将数组转换为链表,并调用 `printList` 函数打印链表中的所有元素。