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

前言

我们可能听过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)
{
    int a = 10;
    int b = 20;
    printf("before swap:a = %d,b = %d\n",a,b);
    swap(a,b);
    printf("after  swap:a = %d,b = %d\n",a,b);
    return 0;
}

运行结果如下:

before swap:a = 10,b = 20
internal swap a = 20,b = 10
after  swap:a = 10,b = 20 

可以看到,a和b的值最终并没有被交换。开始时a,b的值为10,20,而最终还是同样的值。

为什么呢?因为函数参数在传递的时候,都是传原数据的副本,也就是说,swap内部使用的a和b只是最初始a和b的一个副本而已,所以无论在swap函数内部对a和b做任何改变,都不会影响初始的a和b的值。

正因如此,我们常常被告知,不要把直接把结构体直接作为参数,这样效率会很低。由于结构体本身占用字节数较大,如果直接作为参数,那么将会产生一个较大的”副本“,如此一来,效率也就很低了。

我们再结合下面的图来理解:

首先图中方框中的上部分a和b代表了main函数中的a和b,即原始数据,而方框中的下部分a和b代表了函数的参数a和b,即原始数据的“副本”。(后面的图都是如此,上部分代表原始值,下部分代表函数参数值)。

调用swap函数前后的情形如下:

由于在swap中永远只是对a和b的副本进行操作,因此完全不影响原始的a和b的值。最终也不可能达到交换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)
{
    int a = 10;
    int b = 20;
    printf("before swap:a = %d,b = %d\n",a,b);
    swap(&a,&b);
    printf("after  swap:a = %d,b = %d\n",a,b);
    return 0;
}

运行结果:

before swap:a = 10,b = 20
swap a = 20,b = 10
after  swap:a = 20,b = 10 

可以看到在这种情况下,a,b的值才是真正交换了。
看到这里,不知道你是否会疑惑,为什么给函数传递参数的时候,一会是传值,一会是传指针呢?为什么传指针就能改变参数的值呢?实际上,C语言里,参数传递都是值传递!也就是说,你认为的传指针也是传值,只不过它的值是指针类型罢了。

我们再通过图来理解前面为什么传指针就可以交换a,b的值:

从图中可以看出,虽然传递给函数的是指向a和b的指针的副本,但是它的副本同样也是指向a和b,因此虽然不能改变指针的指向,但是能改变参数a和b指向的内容,即改变原始a和b的值。

再看传指针

如果是为指针p申请一段内存,下面的代码能达到目的吗?

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

通过前面的内容分析,肯定是达不到预期效果的。
运行结果:

address of p is (null)

这是为什么呢?我们还是利用前面所知来分析,由于传递给getMemory函数的参数都是一个副本,因此函数内的p也是外部p的一个副本,因此即便在函数内部,将p指向了一块新申请的内存,仍然不会改变外面p的值,即p还是指向NULL。

如何修改呢?我们需要传入p的地址,即指向int类型指针的指针。

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

运行结果如下:

address of p is 0x144f010

从运行结果可以看到,p的值被改变了,而不再是初始的NULL。

可配合下面的图进行理解:

总结

本文总结如下:

  • 函数的参数都是原数据的“副本”,因此在函数内无法改变原数据
  • 函数中参数都是传值,传指针本质上也是传值
  • 如果想要改变入参内容,则需要传该入参的地址(指针和引用都是类似的作用),通过解引用修改其指向的内容
  • 以上结论不限于C语言

原文地址:https://www.cnblogs.com/Hijack-you/p/11962941.html

时间: 2024-11-10 15:34:54

函数参数的传值和传指针有什么区别?的相关文章

函数参数的传值与传址

在如下的主函数里面调用swap函数,问哪一个才能正确交换a,b两变量的值?int main(){    int a=3,b=5;    int *pointer_1 = &a;    int *pointer_2 = &b;    swap(pointer_1,pointer_2);    printf("%d %d",*pointer_1,*pointer_2);    return 0; } /**swap1是正确的,通过地址传递参数,令pointer_1和poin

类对象做函数参数(传值和传引用)

一.传引用 #include "stdafx.h" #include <iostream> using namespace std; class A { public: virtual void show(){cout<<"A"<<endl;} }; class B:public A { public: virtual void show(){cout<<"B"<<endl;} }; c

c++函数参数类型-值,指针,引用

    以" 值传递"方式向函数传递参数 在编写个人函数的时候,你将会受到C++中的一条基本的原则的限制:在默认的情况下,变量只能以值传递的方式传递给函数.这句话的意思是:被传递到函数的只是变量的值,永远不是变量的本身. 例如: void changeValue(int originalValue,int newValue){     originalValue = newValue;   }      int main(){     int myNum=20;     changeV

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

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

传值、传指针、传引用

值传递.指针传递.引用传递的区别 c语言的规则很简单:"所有的参数都是传值调用".在这句话的基础上,我们来分析值传递.指针传递.引用传递的区别. 一.值传递 值传递,这与C函数的性质有关.C函数的所有参数均以"传值调用"方式进行传递,这意味着函数值将获得参数值的一份拷贝,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传给他的参数. 我们先来看实现函数swap1: void swap1(int a,int b) { printf("\n\n传值的示

Java:方法的参数是传值还是传引用

Java中方法的参数总是采用传值的方式. 下列方法欲实现对象的交换,但实际上是不能实现的. public void swap(simpleClass a,simpleClass b){ simpleClass temp=a; a=b; b=a; } 因为传入swap的参数实际是对象a和b的一个拷贝(假设为aa,bb). 在方法中虽然交换了aa和bb,但方法结束后它们不再存在. a和b仍然引用调用swap之前的对象. -------------------------分割线 -----------

Asp:函数是用传值还是传址

传址会把变量的内存地址传递到sub里,在sub里对变量所做的修改就是对原来的变量进行修改,而传值只是把变量的值传递到sub里,此时在sub里修改变量的值不会影响原来的变量. 在vb中调用函数默认是传址(byref),你可以在函数的参数前加byval使用传值方式. Private Sub test(a, ByRef b, ByVal c) a和b都是传址,c是传值 注:在asp中的函数里,默认是传址,如果需要传值,将变量前面加 ByVal 即可.

java传递参数是传值还是传引用

这个问题,网上一查越看越迷糊,纠结完了先总结下 先看个栗子: 1 public class Example { 2  3     public void change(String str1, StringBuffer sbr1, char[] ch1, int a) { 4         str1 = "world"; 5         sbr1.append("world"); 6         ch1[0] = 'g'; 7     } 8  9    

C++数组作为函数参数的几个问题(转)

本文需要解决C++中关于数组的2个问题:1. 数组作为函数参数,传值还是传址?2. 函数参数中的数组元素个数能否确定? 先看下面的代码. #include <iostream> using namespace std; void testArrayArg(int a[]) { cout << endl; cout << "in func..." << endl; cout << "array address: &qu