精华内容
下载资源
问答
  • 形参与实参: 如果把函数比喻成一台机器,那么参数就是...在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。 实参(实际参数) ...

    形参与实参:

    如果把函数比喻成一台机器,那么参数就是原材料,返回值就是最终产品;从一定程度上讲,函数的作用就是根据不同的参数产生不同的返回值。
    C语言函数的参数会出现在两个地方,分别是函数定义处和函数调用处,这两个地方的参数是有区别的。
    形参(形式参数)
    在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。
    实参(实际参数)
    函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。

    形参和实参的功能是传递数据,发生函数调用时,实参的值会传递给形参。

    形参和实参的区别和联系

    1. 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。

    2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。

    3. 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。

    4. 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。

    #include <stdio.h>
    
    //计算从m加到n的值
    int sum(int m, int n) {
        int i;
        for (i = m+1; i <= n; ++i) {
            m += i;
        }
        return m;
    }
    
    int main() {
        int a, b, total;
        printf("Input two numbers: ");
        scanf("%d %d", &a, &b);
        total = sum(a, b);
        printf("a=%d, b=%d\n", a, b);
        printf("total=%d\n", total);
    
        return 0;
    }
    

    运行结果:

    Input two numbers: 1 100↙
    a=1, b=100
    total=5050
    

    在这段代码中,函数定义处的 m、n 是形参,函数调用处的 a、b 是实参。通过 scanf() 可以读取用户输入的数据,并赋值给 a、b,在调用 sum() 函数时,这份数据会传递给形参 m、n。

    从运行情况看,输入 a 值为 1,即实参 a 的值为 1,把这个值传递给函数 sum() 后,形参 m 的初始值也为 1,在函数执行过程中,形参 m 的值变为 5050。函数运行结束后,输出实参 a 的值仍为 1,可见实参的值不会随形参的变化而变化。

    以上调用 sum() 时是将变量作为函数实参,除此以外,你也可以将常量、表达式、函数返回值作为实参,如下所示:

    total = sum(10, 98);  //将常量作为实参
    total = sum(a+10, b-3);  //将表达式作为实参
    total = sum( pow(2,2), abs(-100) );  //将函数返回值作为实参
    
    1. 形参和实参虽然可以同名,但它们之间是相互独立的,互不影响,因为实参在函数外部有效,而形参在函数内部有效。

    更改上面的代码,让实参和形参同名:

    #include <stdio.h>
    
    //计算从m加到n的值
    int sum(int m, int n) {
        int i;
        for (i = m + 1; i <= n; ++i) {
            m += i;
        }
        return m;
    }
    
    int main() {
        int m, n, total;
        printf("Input two numbers: ");
        scanf("%d %d", &m, &n);
        total = sum(m, n);
        printf("m=%d, n=%d\n", m, n);
        printf("total=%d\n", total);
    
        return 0;
    }
    

    运行结果:

    Input two numbers: 1 100
    m=1, n=100
    total=5050
    

    调用 sum() 函数后,函数内部的形参 m 的值已经发生了变化,而函数外部的实参 m 的值依然保持不变,可见它们是相互独立的两个变量,除了传递参数的一瞬间,其它时候是没有瓜葛的。

    指针:
    因为 var 没有在内存中移动位置,所以表达式 &var 是一个常量指针。然而,C 也允仵使用指针类型来定义变量。指针变量存储的是另一个对象或函数的地址。我们后面会进一步讨论指向数组和函数的指针。首先,看看如何声明指向非数组对象的指针,语法如下:

    类型 * [类型限定符列表] 名称 [= 初始化器];
    

    在声明中,星号(*)表示“指向的指针”。标识符“名称”被声明为一个对象,其对象类型为“类型”,也就是“指向‘类型’的指针”。类型限定符为可选择项,可能包含 const、volatile 和 restrict 的任意组合。

    下面是一个简单的例子:

    int *iPtr;                           // 声明iPtr作为一个指向int的指针
    

    int 类型是 iPtr 指针所指向的对象的类型。为了让指针能够引用到给定的对象,将该对象的地址赋值给该指针。例如,如果 iVar 是一个 int 变量,那么下面的赋值操作会让 iPtr 指向变量 iVar:

    iPtr = &iVar;                         // 使得iPtr指向变量iVar
    

    一般形式的声明包含了用逗号分隔开的声明符列表,每个声明符声明了各自的标识符。在指针声明中,星号(*)是声明符的一部分。我们可以在一个声明中同时定义和初始化变量 iVar 和 iPtr,如下所示:

    int iVar = 77, *iPtr = &iVar;  // 定义一个int变量,以及一个指向它的指针
    

    这两个声明中的第二个声明,将 iPtr 初始化为变量 iVar 的地址,这使得 iPtr 指向 iVar。图 1 展示了变量 ivar 和 iPtr 在内存中可能的排列方式。这里的地址为示例所需,纯粹是虚构的。如图所示,存储在指针 iPtr 中的值是对象 iVar 的地址。
    在这里插入图片描述
    在验证与调试时,输出地址常常有助于判断。函数 printf()提供了一种格式化修饰符:%p。下面语句输出变量 iPtr 的地址和内容:

    printf("Value of iPtr (i.e. the address of iVar):       %p\n"
          "Address of iPtr:                                 %p\n", iPtr, &iPtr);
    

    无论指针所指对象的类型是什么,在内存中指针空间的大小(例如,表达式 sizeof(iPtr))都是一样的。换句话说,char 指针所占用的空间和指向大型结构的指针所占用的空间一样。在 32 位计算机上,指针通常是 4 个字节长。

    按值传递与按址传递:
    按值传递:是把实参的值赋值给形参,相当于copy。那么对形参的修改,不会影响实参的值 。
    按址传递: 是传值的一种特殊方式,只是他传递的是地址,不是普通的赋值,那么传地址以后,实参和行参都指向同一个对象,因此对形参的修改会影响到实参。

    void exchg_address(int *px,int *py){
    //此类是针对地址处的数值进行变化,指针关系不动	
    	int tmp=*px;
    	*px=*py;//地址处的数值变化 
    	*py=tmp;
     }
    //此类仅仅转换地址,地址处的数值并无变化 
    void exchg_value(int px,int py){
    int temp;
    tmp=px;
    px=py;
    py=tmp;
    }
    int main()
    {
    #if 0
    	int a=4,b=6;
    	exchg(&a,&b);                           
    	printf("a=%d,b=%d。\n",a,b);   //a=6,b=4
    	return 0; 
    #endif 
    	int a=4,b=6;
    	exchg(a,b);                               
    	printf("a=%d,b=%d。\n",a,b);  //a=4,b=6
    	return 0; 
    } 
    
    
    展开全文
  • 发现在函数体中的修改 , 对对象本身的发生了改变 , 在函数之外 , 该列表的内容依然发生了改变 , 这是事先就能猜测到的结果 , 因为python中的参数 , 传入的是变量引用的副本 , 它与变量指向同一个.  鉴于前三种...

    我的机器学习教程「美团」算法工程师带你入门机器学习  以及 「三分钟系列」数据结构与算法  已经开始更新了,欢迎大家订阅~这篇专栏整合了这几年的算法知识,简单易懂,也将是我实体书的BLOG版。

    欢迎大家扫码关注微信公众号「图灵的猫」,除了有更多AI、算法、Python相关文章分享,还有免费的SSR节点和外网学习资料。其他平台(微信/知乎/B站)也是同名「图灵的猫」,不要迷路哦~

     

    最近几天在看python , 看到函数这一段的时候 , 突然发现一个问题 , 字符串在函数体中重新赋值 , 不会对函数体外 , 该字符串本身的值产生影响 . 
    例如 :

    def changestr (str):
        str = "inside" 
        print("这是function中 , 值为:",str)
    mystr = "outside"
    changestr(mystr)
    print("这是函数外边 , 值为:",mystr)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行结果为 :

    这是function中 , 值为: inside
    这是函数外边 , 值为: outside
    • 1
    • 2

    考虑到字符串不可变的特性 (python的六种标准类型 , 其中有三种是不可变的类型 , 即 : Number ,String , Tuple) , 我分别测试了Number和Tuple , 发现同以上结果是一样的 , 因为这三种类型只能通过重新赋值来改变对象的值 , 另外三种类型(List , Set , Dictionary) 是可以改变内部的元素 , 于是又测试了这三种seq类型 , 第一次测试如下 :

    def change(mylist):
        mylist.append([1,2,3,4])
    mylist = ["aa",21]
    print(mylist)
    change(mylist)
    print(mylist)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果如下:

    ['aa', 21]
    ['aa', 21, [1, 2, 3, 4]]
    • 1
    • 2

    发现在函数体中的修改 , 对对象本身的值发生了改变 , 在函数之外 , 该列表的内容依然发生了改变 , 这是事先就能猜测到的结果 , 因为python中的参数 , 传入的是变量引用的副本 , 它与变量指向同一个值. 
    鉴于前三种类型的参数是直接重新赋值的 , 于是继续下一步测试 :

    def change2(list):
        list = [1,2,3,4]
    mylist = ["aa",21]
    print(mylist)
    change2(mylist)
    print(mylist)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果如下:

    ['aa', 21]
    ['aa', 21]
    • 1
    • 2

    有些意外 , 出现了和三种不可变类型参数一样的情况 , 在函数体中的重新赋值 , 没有对外部变量的值产生影响 , 不过仔细一想 , 却又在情理之中 . 
    我对python还不够熟悉 , 但是从近两天的学习中发现 , 其存储模型与java相似 , 即变量中存储的是引用 , 是指向真正内容的内存地址(当然 ,java中的八大基本数据类型 , 变量名和值都是存储在堆栈中的 ) , 对变量重新赋值 , 相当于修改了变量副本存储的内存地址 , 而这时的变量已经和函数体外的变量不是同一个了, 在函数体之外的变量 , 依旧存储的是原本的内存地址 , 其值自然没有发生改变 .

    简单来说 : 
    - 函数体传入的参数 , 为函数体外变量引用的副本 . 
    - 在函数体中改变变量指向的堆中的值 , 对函数外变量有效. 
    - 在函数体中改变变量的引用 , 对函数外变量无效

    大致图如下 :(红色线代表重新赋值动作) 
    这里写图片描述

    展开全文
  • 什么时候使用按值传递? 1 对于使用传递的值而不做修改的函数 (1)数据对象较小,如内置数据类型或小型结构体,则按值传递 void func(int ); (2)数据对象是数组,则只能使用指针,并将指针const指针 void func...

    在C++中,函数的形参,什么时候使用引用?什么时候使用指针?什么时候使用按值传递?

    1 对于使用传递的值而不做修改的函数

    (1)数据对象较小,如内置数据类型或小型结构体,则按值传递

    void func(int );

    (2)数据对象是数组,则只能使用指针,并将指针const指针

    void func(const int *,int);//第二个参数为数组长度

    (3)数据对象是较大的结构体,则const指针或const引用都行

    struct struc{…};

    void func(const struc *);

    或void func(const struc &);

    (4)数据对象是类,则使用const引用

    void func(const string &,);

    2 对于使用传递的值要做修改的函数

    (1)数据对象是内置数据类型,则使用指针

    void func(int *);

    (2)数据对象是数组,则只能使用指针

    void func(int *,int);//第二个参数为数组长度

    (3)数据对象是结构体,则使用引用或指针

    struct struc{…};

    void func(struc *);

    或void func(struc &);

    (3)数据对象是类,则使用引用

    void func(ostream &);

    展开全文
  • 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。   按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数...
  • 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。   按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这
  • 1.按值传递:实参传递给形参的是值,实参和形参在内存上是两个独立的变量,修改形参不会对实参产生影响。原理如下图: 2.按引用传递:实参传递给形参的是引用地址,实参和形参在内存上指向堆内存中同一个数据...

    实参给形参传递数据的两种方式:

    1.按值传递:实参传递给形参的是值,实参和形参在内存上是两个独立的变量,修改形参不会对实参产生影响。原理如下图:

                                            

     

    2.按引用传递:实参传递给形参的是引用地址,实参和形参在内存上指向堆内存中同一个数据,修改形参会对实参产生影响。 其原理图如下:

     

                                     

     

                                     

    [小结]:

    1.参数是基本数据类型:按值传递;引用类型:按引用传递 

    2.自定义的类是引用类型 

    3.对象作为方法参数,修改形参的属性会影响实参

    4.对象数组作为方法参数,修改形参会影响实参

    5.可变参数作为方法参数,修改形参会影响实参 

    展开全文
  • 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。  按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数...
  • 最近遇到个有趣的问题:“JS中的值是按值传递,还是按引用传递呢?” 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略...
  • 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略...按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传
  • 0.0按值传递的是指在方法调用时,传递的参数是实参数值的副本.按引用传递指的是在方法调用时,传递的参数是实参的引用,也可以理解为实参所对应的内存空间的地址.为了理解java语言中的值传递与引用传递,首先给出下面的...
  • 引用传递与按值传递

    2019-03-18 16:54:03
    按值传递:方法定义时,如果形参是基本类型,那么参数就是按值传递 引用传递:如果形参是引用类型,那么参数就是引用传递 按值传递和按引用传递有什么区别呢? 按值传递:传递变量的值(也可以理解传递的变量的副本...
  • 在开始JavaScript按值传递与按共享传递的探讨之前,我们先要知道JavaScript的五种... - 按值传递:函数的形参是被调用时所传实参的副本,修改形并不会影响实参。 - 按引用传递:函数的形参接收实参的内存地址,而不再
  • 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略...按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 677
精华内容 270
关键字:

形参按值传递