传值、传指针、传引用

值传递、指针传递、引用传递的区别

c语言的规则很简单:“所有的参数都是传值调用”。在这句话的基础上,我们来分析值传递、指针传递、引用传递的区别。

一、值传递

值传递,这与C函数的性质有关。C函数的所有参数均以“传值调用”方式进行传递,这意味着函数值将获得参数值的一份拷贝,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传给他的参数。

我们先来看实现函数swap1:

void swap1(int a,int b)
{
    printf("\n\n传值的示例函数swap1\n");
    printf("形参a的地址:%p\n",&a);
    printf("形参b的地址:%p\n",&b);
    int temp;
    temp = a;
    a = b;
    b = temp;
    printf("形参a的值:%d  ",a);
    printf("形参b的值:%d  ",b);
}

main函数如下:

    int a,b;
    a = 5;
    b = 6;
    printf("实参a的值:%d  ",a);
    printf("实参a的地址:%p\n",&a);
    printf("实参b的值:%d  ",b);
    printf("实参b的地址:%p\n",&b);
    swap1(a,b);
    printf("\n\n传值交换后a,b的值:\n");
    printf("实参a的值:%d  \n",a);
    printf("实参b的值:%d  \n",b);

执行结果如下:

实参a的地址为:0018FF44,实参b的地址为0018FF40

栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。程序结束时由编译器自动释放。因此,参数a,b是存放在栈区的。在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。因此,我们会看到a,b的地址是呈减少的趋势。

因此,按照“值传递”的思想,形参是实参的拷贝,程序会开辟一块新的栈区为形参。它们进行交换操作是在这块新的栈区里面,并不影响实参的那一块内存。

执行完swap1函数,形参的内存中a,b的值发生了变化,但并不影响实参的的值。

在函数调用时,参数是从右到左读的,所以b的地址比a的地址高

二、指针传递

我们来看实现函数swap2:

void swap2(int *a,int *b)
{
    printf("\n\n传指针的示例函数swap2\n");
    printf("形参a的地址:%p\n",&a);
    printf("形参b的地址:%p\n",&b);
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
    printf("形参a的值:%p  ",a);
    printf("形参b的值:%p  ",b);
}
main函数调用:
    swap2(&a,&b);
    printf("\n\n传指针交换后a,b的值:\n");
    printf("实参a的值:%d  \n",a);
    printf("实参b的值:%d  \n",b);

运行结果:

因此我们可以看到,形参的值实际是实参的地址,因为c语言是值传递,main函数中传入的是&a,&b即a,b的地址,所以形参中存储的也是a和b的地址。

然后执行:

temp = *a;
*a = *b;
*b = temp;

*a是什么意思?*a是地址为a的内存中所存储的函数值。所以上面三条语句的意思就是地址为a的内存中所存储的函数值与地址为b的内存中所存储的函数值进行交换。如下图:

因此,他修改的是实参的内容。执行完后实参的内容应该是这样的:

所以,这个时候a的值为6,b的值为5。

三、引用传递

我们来看实现函数swap3:

void swap3(int &a,int &b)
{
    printf("\n\n传引用的示例函数swap3\n");
    printf("形参a的地址:%p\n",&a);
    printf("形参b的地址:%p\n",&b);
    int temp;
    temp = a;
    a = b;
    b = temp;
    printf("形参a的值:%d  ",a);
    printf("形参b的值:%d  ",b);
}
main函数调用:

swap3(a,b);
printf("\n\n传引用交换后a,b的值:\n");
printf("实参a的值:%d  \n",a);
printf("实参b的值:%d  \n",b);

执行结果截图:

我们可以看到形参的地址与实参的地址相同,调出汇编代码:

71:       swap3(a,b);
0040D9B8   lea         eax,[ebp-8]
0040D9BB   push        eax
0040D9BC   lea         ecx,[ebp-4]
0040D9BF   push        ecx
0040D9C0   call        @ILT+15(swap3) (00401014)
0040D9C5   add         esp,8

lea eax,[ebp-8]是将b的偏移地址送入eax,

这个是swap2的汇编代码,我们可以看到二者是一样的。

66:       swap2(&a,&b);
0040D979   lea         edx,[ebp-8]
0040D97C   push        edx
0040D97D   lea         eax,[ebp-4]
0040D980   push        eax
0040D981   call        @ILT+10(swap2) (0040100f)
0040D986   add         esp,8

所以,对于这里的swap3和前面的swap2来讲,堆栈中的函数参数存放的都是地址,在函数中操作的方式是一致的。但是,对swap3来说这个地址是主调函数通过将实参变量的偏移地址压栈而传递进来的–这是引用传递;而对swap2来说,这个地址是主调函数通过将实参变量的值压栈而传递进来的–这是值传递,只不过由于这个实参变量是指针变量所以其值是地址而已。

  这里的关键点在于,同样是地址,一个是引用传递中的变量地址,一个是值传递中的指针变量的值。

四、总结

其实指针传递和值传递本质都是值传递,值传递是传输了要传递的变量的一个副本。在C++中有引用传递,就是将要传递的变量的地址传到了被调用函数中,如果在被调用函数中改变,那么就会在调用函数中改变。

Reference:

1.函数参数是如何传递的

2.《C和指针》第七章函数。

时间: 2024-11-07 14:26:03

传值、传指针、传引用的相关文章

函数 传值调用 指针调用 引用调用

#include <iostream> using namespace std; int max(int num1, int num2); void swap(int *x, int *y); void swap(int &x, int &y); int main(){ cout << "hello world" << endl; int num1 = 100; int num2 = 200; int v_max = max(num1

函数传指针

函数传指针与引用 由于在今天编程的时候遇到一个小小的问题,这个问是虽然小,但是闲扰了我一整天的时间------注意,是一整天! 废话不多说,先给出一段代码 // 二叉树的建立.cpp : 定义控制台应用程序的入口点. // #include <iostream> using namespace std; //定义二叉排序树的节点 typedef struct Node { int data; struct Node * lchild;//左孩子 struct Node * rchild;//右

深刻理解C#的传值调用和传引用调用

传值调用和传引用调用是几乎所有主流语言都会涉及到的问题,下面我谈谈我对C#中传值调用和传引用调用的理解. 1. 一般对C#中传值调用和传引用调用的理解 如果传递的参数是基元类型(int,float等)或结构体(struct),那么就是传值调用. 如果传递的参数是类(class)那么就是传引用调用. 如果传递的参数前有ref或者out关键字,那么就是传引用调用. 验证示例的代码如下: view sourceprint? 01    using System; 02 03    public cla

传指针和传指针引用的区别/指针和引用的区别(本质)

转自:http://blog.sina.com.cn/s/blog_673ef8130100imsp.html 指针传递参数本质上是值传递的方式,它所传递的是一个地址值.值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本.值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值.(这里是在说实参指针本身的地址值不会变) 而在引用传递过程中,被调函数的形式参数虽然也作为局

函数参数的传值和传指针有什么区别?

前言 我们可能听过C语言中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了. 传值 我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的: #include<stdio.h> void swap(int a,int b) { int temp = a; a = b; b = temp; printf("swap a = %d,b = %d\n",a,b); } int main(void) {

关于函数传参--传指针,传引用

今天和同学讨论到指针和引用的传递问题,有些想法从推理上讲是正确的,但是因为是推理,说出自己观点的时候不是那么有底气,本着实践是检验真理的唯一标准的原则,在电脑上敲了几段代码,验证了推理的正确性. 先上代码,再分析. 代码1: void Swap0(int a1,int b1){ int temp; temp=a1; a1=b1; b1=temp; } void Swap1(int *a1,int *b1){ //交换地址 int *temp; temp=a1; a1=b1; b1=a1; } v

引用传参与指针传参的区别

Reference: https://blog.csdn.net/u013130743/article/details/80806179 概念: 引用传参:引用是变量的别名.引用传参传递进形参列表的是实参的别名,在函数的执行中也会在栈空间上开辟存储空间,存储形参的地址 (也就是实参的地址).对形参的任何操作都会间接寻址到实参.也就是在函数中对形参变量做的任何改变都会影响到实参变量. 指针传参:指针传参本质上是值传参.指针在形参列表中传递的是实参的地址.也就是说,指针的值的实参的地址.在函数的执行

再看传指针

#include<stdio.h> #include<stdlib.h> void getMemery(int *pt) { printf("pt=%p\n",pt); printf("&pt=%p\n",&pt); /*申请1024个int大小*/ pt = malloc(sizeof(int) * 1024); if (NULL == pt) { printf("malloc failed\n"); p

[编程开发] 由指针传參引发的一点分析

昨天有同学(初学指针)在练习单链表和二叉树的时候,程序老是崩溃,或者得不到正确结果,于是向我求助.问题就出在指针的參数传递上,没传好指针导致内存混乱,其它代码基本全对.这个错误十分可惜.故在此我想做个记录,可能显得十分基础. 假设函数的參数是普通的一级指针,那么就意味着你仅仅能使用指针.改变指针指向或者改变指向的目标变量.不能试图通过这个指针来申请内存. void getMemory(int *p) { p = (int *)malloc(sizeof(int) * 10); } void fu