精华内容
下载资源
问答
  • 最近在讨论着两个看上去学c的人都知道的东西,不就是指针嘛,比变量高级一点而已。还有实参和形参,不就是函数使用的那些东东嘛。...1. 函数发生调用时实参和形参都会占用内存吗???   #include int

    最近在讨论着两个看上去学c的人都知道的东西,不就是指针嘛,比变量高级一点而已。还有实参和形参,不就是函数使用时的那些东东嘛。

    话说后者在csdn发了个帖子,大家讨论了一下,发现很多人C语言的基础很扎实,而且汇编的功底也很强。前者的话只是我个人的学习总结而已。现在先来讨论讨论后者吧。

    我结合一下帖子。

    1. 函数发生调用时,实参和形参都会占用内存吗???

     

    #include <stdio.h>
     int fun(int a);
     void main()
     {
     int a=1;
     fun(a);
     printf("%d\n",fun(1));
    
     printf("%d\n",a);
     }
     
    int fun(int a)
     {
     //a=3;
     a=a+1;
     return a;
     }

    贴上x86下面的汇编看看:

    恩,我贴这个汇编出来看下吧:
    .file "app.c"
     .section .rodata
     .LC0:
     .string "%d\n"
     .text
     .globl main
     .type main, @function
     main:
     pushl %ebp
     movl %esp, %ebp
     andl $-16, %esp
     subl $32, %esp
     movl $1, 28(%esp)
     movl 28(%esp), %eax
     movl %eax, (%esp)
     call fun
     movl $1, (%esp)
     call fun
     movl $.LC0, %edx
     movl %eax, 4(%esp)
     movl %edx, (%esp)
     call printf
     movl $.LC0, %eax
     movl 28(%esp), %edx
     movl %edx, 4(%esp)
     movl %eax, (%esp)
     call printf
     leave
     ret
     .size main, .-main
     .globl fun
     .type fun, @function
     fun:
     pushl %ebp
     movl %esp, %ebp
     addl $1, 8(%ebp)
     movl 8(%ebp), %eax
     popl %ebp
     ret
     .size fun, .-fun
     .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
     .section .note.GNU-stack,"",@progbits 


    代码卡上去都很简单明了,然后然后就是各种讨论了:

    先给出官方的说法,应该是书上普遍的说法吧:

    函数的形参和实参具有以下特点:  
     1. 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。  
     2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。 
     3. 实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。  
     4. 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。

    比较满意的答案:

    a.

    我的理解,这和不同CPU架构的编译器有关
     以ARM为例,调用函数的参数都是都过ATPCS规则传递的,参数少于4个,使用r0-r3四个CPU内部通用寄存器(不在内存),多于四个就要使用栈了,这个时候才占内存
     其他架构的cpu肯定会不同 

    b.

    我的理解,这和不同CPU架构的编译器有关
     以ARM为例,调用函数的参数都是都过ATPCS规则传递的,参数少于4个,使用r0-r3四个CPU内部通用寄存器(不在内存),多于四个就要使用栈了,这个时候才占内存
     其他架构的cpu肯定会不同
     

    c.

    我理解的是在汇编里面没有实和形的概念,只是对内存的操作罢了。
     下面的代码和汇编
     #include <stdio.h>
     int fun(int a);
     void main()
     {
     int a=1;
     fun(a);
     // printf("%d\n",fun(1));
     
    // printf("%d\n",a);
     }
     
    int fun(int a)
     {
     //a=3;
     a=a+1;
     return a;
     }
     .file "app.c"
     .text
     .globl main
     .type main, @function
     main:
     pushl %ebp
     movl %esp, %ebp
     andl $-16, %esp
     subl $32, %esp      //分配栈空间 给局变量以及要调用的函数的形参
     movl $1, 28(%esp)   //给局部变量a赋值1 即 a = 1;
     movl 28(%esp), %eax // 指令不支持双存储器操作数(即不再ss型指令)同过register过渡,(SR型)
     movl %eax, (%esp)   //给形参赋值, 形参a的地址是%esp, 实参a的地址时%esp + 28;
                            //参数和局部变量的入栈未必要通过push。
     call fun
     leave
     ret
     .size main, .-main
     
    .globl fun
     .type fun, @function
     fun:
     pushl %ebp
     movl %esp, %ebp
     addl $1, 8(%ebp)  //将形参进行+1操作。
     movl 8(%ebp), %eax
     popl %ebp
     ret
     .size fun, .-fun
     .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
     .section .note.GNU-stack,"",@progbits
     
    
    其实汇编代码说的很清楚了。
     
    X86架构下,栈的增长方向是向地址方向生长的。
     
    每个函数相当于一层,在汇编中,我称之为一个frame。
     该frame 有一个基地址,就是ebp的值,还有一个栈顶指针esp。
     所以函数调用中的,前两条指令一般都是:
     AT&T的格式:
     pushl %ebp;  //保存上一层的frame的的基地址
     movl %esp, %ebp; //子函数的基地址。
     
    Intel格式:
     push ebp;
     mov ebp,esp;
     
    由于栈是向低地址生长的,所以子函数中
     addl $1, 8(%ebp)  //将形参进行+1操作。
     此时形参的a的地址是 ebp+8,因为call 转子函数时会将返回地址,即call指令的下一条指令的地址
     (eip+5)入栈,32位的地址,4字节; 即 eps -= 4; 还有pushl %ebp; 又有esp -=4;
     所以esp+8 就是形参a的地址。
     
    对于一个子函数来说,一般 [ebp + n*4](n >= 1)是形参, [ebp-4*n](n>=1)是该子函数的局部变量。

          其实从那段简单的汇编角度来看,个人理解可以发现如帖子里面所的会根据编译器和cpu来决定,比如这个x86就简单的使用esp栈指针,ebp基指来完成内存的操作。在这边我的意愿是吧理解成这样:在调用fun之前的那个阶段,会根据你的形参从右到左依次从局部变量的内存空间copy到新的栈空间,这个空间理解为了栈帧。所以在esp继续移动之前,我们发现当下栈顶放的是几个形参的内容,这里我就理解为形参,实际也没必要去想是形参。但是这个空间可以确定不是函数运行完就释放的。这个查了一下所谓的函数调用约定有如下一句:__cdecl 参数自右向左压栈;如果有this指针,作为最后一个参数压栈,调用者清栈。而我们默认的就是这种约定,从这个角度我的理解就是这个栈空间啊,得由main来完成清栈而不是被调的fun来完成。

    总结出来就是这个形参开不开内存,关键还是看自己的程序,CPU架构,编译器是否优化,函数调用的约定性等几点,最后这个形参是不是函数调用结束就回收也得看上面的几点。比较同意上面c的观点。在汇编阶段没有必要去分实参和形参了,都是对内存操作。

    结贴哈哈。

     

    2.另一个问题

    *p和**p,这个普通的fun(*p),fun(**p)。

    在函数中调用时,这几个指针的内存分配已经在函数内部对形参的操作,先给个内存的分配图

    这图展示的是最简单的内存分配了,黄色区域来至于变量如果fun(*),那么就直接传入p,fun(**)就直接fun(&p),这里充分说明了函数是传值调用,最复杂的指针工作也是一个地址数值。

    a.这里可以看到在fun(*)里面如果要改变p的数值那是可以的但是只是修改属于fun里面的p即黄色区域,这块可以从上面的问题中理解为给fun用的内存块,是对局部变量p的copy。所以这很好的说明了在函数返回后p还是=&a,并没有发生变化。因此这个说明了一级指针调用的函数内,对p操作对外部没有意义,使用*p来操作这个红色区域这倒可以。当然只是一种方式而已,只是结合着上面的问题引发的。就这样而已

    下面来说通过2级指针修改p,fun(**p)调用传入的是黄色区域的&p那么函数内部如果给*p赋数,那么返回后p就发生了变化指向了别的地址空间。这个是2级操作的好处。

    这个简单的理解的好处就是可以让你对函数内外的变量在内存中的存在与可用性进一步加深了理解。

     

    学习还是得深入,多写代码,多看代码,写出自己的高质量代码。

     

     

     

     

     

     

     

     

     


     


     

     

    展开全文
  • C语言中,关于形参与实参说明...4)对应的实参和形参是两个独立实体,它们之间只有单向的值的传递,即实参的值传递给形参。形参的值在函数中不论怎么改变,都不会影响实参。 以上来自 C语言程序设计(第三版)/杨路.

    在C语言中,关于形参与实参说明如下。

    1) 形参是变量,但是函数被调用时才被分配内存。当函数执行完毕返回时,形参占用的内存空间又被释放。

    2)实参可以是变量、常量和表达式,但实参必须有确定的值。

    3)形参和实参的类型必须一致。

    4)对应的实参和形参是两个独立实体,它们之间只有单向的值的传递,即实参的值传递给形参。形参的值在函数中不论怎么改变,都不会影响实参。

    以上来自 C语言程序设计(第三版)/杨路明主编

    个人观点:虽然在C语言中形参实参按值传递,但是按形参实参类型又可分为:值类型(数值类型、char型、struct及enum类型),引用类型(指针),这会影响参数传递是否会影响到原来的值。

    展开全文
  • C语言函数传递(实参 形参

    万次阅读 多人点赞 2016-07-06 20:34:19
    形参:在被调函数形参表中的变量名、数组名,该过程在被调用前,没有为它们分配内存,其作用是说明自变量的... 传递参数形参实参是按位置结合的,形参和实参表中对应的变量名可以不必相同,但它们的数据类型

    形参:在被调函数形参表中的变量名、数组名,该过程在被调用前,没有为它们分配内存,其作用是说明自变量的类型和形态以及在过程中的作用。
    形参可以是 合法变量名,数组名
    实参:从主调过程传递给被调用过程的参数值。实参可以是变量名、数组名、常数或表达式。在过程调用
    传递参数时,形参与实参是按位置结合的,形参表和实参表中对应的变量名可以不必相同,但它们的数据类型、参数个数及位置必须一一对应
    两者关系:
    1. 形参只能是变量,实参可以是常量、变量或表达式。在被定义的函数中,必须指定形参的类型。
    2. 实参与形参的个数应一样,类型应一致。字符型和整型可以互相通用。
    3. 在调用函数时,如果实参是数组名,则传递给形参的是数组的首地址
    4.实参传递给形参是单向传递,形参变量在未出现函数调用时,并不占用内存,只在调用时才占用。调用结束后,将释放内存。执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数中的实参的值。
    5.形参如同公式中的符号,实参就是符号具体的值,在调用过程前必须得到赋值;调用过程就是实现形参与实参的结合,把实参的值通过调用传递给形参,相当于把值代入公式进行计算。

    C语言中在发生有参函数调用时,实参变量与形参变量之间的数据都是单向的“值传递”方式。包括指针变量和数组名作参数的情况。
    C语言要求函数的实参要有确定的值,在函数调用时给形参分配相应的内存单元,同时将实参的“值”赋(复制)给形参,实现数据从实参到形参的传递(‘值传递’方式)。因为是复制,所以在操作副本(形参)过程中不会影响到原本(实参)内容。
    首先,作为函数实参的量包括常量、变量和表达式。其中变量又包括简单变量、数组元素、数组名、指针变量等。不同类型变量作参数实现的数据传递方式相同,效果不同。所谓方式相同即都是参数间数据单向的“值传递”,效果不同是指被调函数能否改变主调函数中变量的值。

    情况一:简单变量或数组元素作为函数参数
    数组元素本身属于简单变量,在向形参传递数据时,根据前述规则只需将变量中的“值”复制一份放到形参变量中去操作,此时在被调用函数中操作的对象(形参)与实参并不在同一内存单元,并且在调用结束后形参所占内存单元被释放,因此调用函数不会影响到实参变量的值。同时被调函数也不会影响到主调函数中其他变量的值。
    例:1

    void main()
    {
    int a=1,
    f(int a);
    printf(“%d%d”,a,f(a));
    }
    f(int a)
    {
    return(++a);
    }
    例2

    void main()
    {
    int a[3]={1,2,3};
    f(int a);
    printf(“%d%d”,a[0],f(a[0]));
    }
    f(int a)
    {
    return(++a);
    }

    情况二:指针变量或数组名作为函数参数
    1.指针变量作函数参数
    指针变量作实参在调用时仍然符合前述“值传递”规则,将其“值”赋给形参,相当于复制。此时数据在实参与形参间传递仍是单向的,调用函数不会影响实参的“值”(即指针变量中所存地址)。而与简单变量不同的是指针变量复制给形参的“值”本身是一个地址,这个地址为形参访问其所指变量创造了可靠条件。我的理解是,实参是一个抽屉的钥匙,在传参时,实参复制了一把钥匙传给形参。而被调函数拿到钥匙副本后,进行的操作可以分为两类:1、对钥匙本身做了一些操作(对指针本身进行操作);2、通过钥匙对抽屉里的内容进行了一些操作( 对指针所指的变量进行操作);两种操作都不可能影响实参的值(即钥匙原本),却有可能改变实参所指向变量的值(即抽屉里的内容)。
    例1

    void main()
    {
    void swap(int*p1,int*p2);
    int a,b;
    int *pointer1,int*pointer2;
    scanf(“%d,%d”,&a,&b);
    pointer1=&a; pointer2=&b;
    if(a

    展开全文
  • 编译器linux下面的gcc, #include <stdio.h> int fun(int a); void main() { int a=1; fun(a); printf("%d\n",fun(1)); printf("%d\n",a); } ...这个行参占不占内存,问问
  • C语言中的实参和形参

    千次阅读 2018-11-24 21:57:46
    形参:定义函数名函数体时需要用的参数,目的是用来接收调用函数时传递的参数 1.形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 2.实参...

    个人理解,如有错误还请指正

    实参:传递给被调用函数的值;

    形参:定义函数名和函数体时需要用的参数,目的是用来接收调用该函数时传递的参数

    1.形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。
    2.实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。

    C语言要求函数的实参要有确定的值,在函数调用时给形参分配相应的内存单元,同时将实参的“值”赋(相当于拷贝)给形参,实现数据从实参到形参的传递(‘值传递’方式)。因为是复制,所以在操作副本(形参)过程中不会影响到原本(实参)内容。

    下面提供两个代码,用于理解调用函数时参数的传递:

    程序一:不会改变实参的值

    #include <stdio.h>

    void exchange(int a,int b)
    {
        printf("func:a:%d,b:%d\n",&a,&b);
        a = a+b;
        b=a-b;
        a=a-b;
        printf("func--a:%d,b:%d\n",a,b);

    }
    int main(void)
    {
        int a=3,b=5;
        printf("main:a:%d,b:%d\n",&a,&b);
        exchange(a,b);

        printf("main--a:%d,b:%d\n",a,b);
        return 0;
        
    }

    程序二:改变了实参的值

    #include <stdio.h>

    void Bubble_Sort(int *a,int len)
    {//在写这个函数的时候,突然想到一个问题,怎么求传递过来数组的大小,而不使用len这个形参呢?

    //如果是个字符型指针,传递过来一个字符串,还可以用strlen()函数来求,但是这是int型数组。
        int i=0,j=0,cnt=0;
        char flag = 0;
        for(i=0;i<len;i++)
        {
            printf("addr:%d\n",a+i);
        }
        for(i=len-1;i>=0;--i)
        {
            for(j=0;j<i;++j){
                if(a[j]>a[j+1]){
                    a[j] = a[j]+a[j+1];
                    a[j+1] = a[j] - a[j+1];
                    a[j] = a[j] - a[j+1];
                    flag = 1;
                    cnt++;
                }
            }
            if(0 == flag){
                printf("cnt:%d\n",cnt);
                return ;
            }
            else
                flag = 0;
        }
    }

    int main(void)
    {
        int i = 0;
        int a[5]={0,5,2,4,3};
        for(;i<sizeof(a)/sizeof(int);i++)
        {
            printf("main_addr:%d\n",&a[i]);
        }
        Bubble_Sort(a,5);

        for(i =0;i<5;i++)
            printf("%d\t",a[i]);
        printf("\n");

        return 0;
        
    }

     

    展开全文
  • C语言实参和形参区别

    千次阅读 2019-03-16 22:16:46
    C语言函数的参数会出现在两个地方,分别是函数定义处和函数调用处,这两个地方的参数是有区别的。 形参(形式参数) 在函数定义中出现的参数可以看做是一个占位符...形参和实参的功能是传递数据,发生函数调用时,实...
  • 实参: 即实际参数,是在调用时...在调用函数时实参会赋值给形参。所以要注意实参的个数,类型必须与形参一一对应,且实参要预先有确定值。可以理解为形参时函数调用时用于接收参数值的变量。 区别: 形参出现在函
  • 实参:即你调用函数时传递的参数。实参可以是常量、变量、表达式。 注意:形参和实参实际上占用的是两份不同的存储单元。 形参和实参之间的传递 在c语言中,实参形参的数据传递是“值传递”,单向传递,只能由...
  • C语言实参和形参的类型不匹配

    千次阅读 2021-01-08 11:32:50
    #include <...对于参数类型的不匹配,有一个解释是:函数调用时,将实参的值传递给形参,系统首先会自动隐式地把实参的值的类型转换为形参的类型,然后再赋值给形参。 对于缺少被调函数的类型说明,但程序依
  • C语言实参和形参之间的数据传递是单向的“值传递”,单向传递,只能由实参传给形参,反之不行 输出格式并没有选择%p而是选择了%d,因为十进制的数看起来更直观 1 值传递 如果只是传递值的话: #include //值...
  • 发生函数调用时,主调函数实参的值传送给被调函数形参从而实现主调函数向被调函数的数据传送。 函数形参和实参具有以下特点: 1.形参变量只有在被调用时才分配内存单元,在调用结束,即刻释放所分配的内存...
  • C语言形参和实参

    2016-10-29 20:59:00
    就是调用函数的时候,调用函数时写在函数名小括号后面的参数就叫实参 2).注意项 a.实参既可以放常量,也可以放变量,还可以放表达式;形参:它其实可以理解为就是这个函数的局部变量 b.实参形参传值,其实只....
  • C语言函数-形参和实参

    千次阅读 多人点赞 2019-04-29 09:34:34
    在调用传递给函数的参数,即传递给被调用函数的值,会被被调用函数内部使用,所以称为实际参数; 2.形参和实参的区别与联系 形参和实参的区别: 1): 形参变量在函数未被调用是不占内存空间的,只有在函数被...
  • 形式参数实际参数 函数的参数分为形参和实参两种。在本小节中,进一步介绍形参...发生函数调用时,主调函数实参的值传送给被调函数形参从而实现主调函数向被调函数的数据传送。 函数形参和实参具有以下特点:
  • c语言形参和实参的区别?

    万次阅读 2014-07-04 11:26:37
    形式参数实际参数 函数的参数分为形参和实参两种。...发生函数调用时,主调函数实参的值传送给被调函数形参从而实现主调函数向被调函数的数据传送。 函数形参和实参具有以下特点: 1. 形
  • 发生函数调用时, 主调函数实参的值传送给被调函数形参从而实现主调函数向被调函数的数据传送。 1.形参变量只有在被调用时才分配内存单元,在调用结束, 即刻释放所分配的内存单元。因此,形参只有在函数...
  • 上一个例程中在进行函数调用的时候,不需要任何参数传递,所以函数定义调用时括号内都是空的,但是更多的时候我们需要在主调函数调用函数之间传递参数。在调用一个有参数的函数时,函数名后边括号中的参数叫做...
  • c语言形参实参的关系

    千次阅读 多人点赞 2018-04-11 16:10:48
    一、形参出现在函数定义中,在整个函数体内都可以使用,形参变量只有在被调用时才分配内存单元,在调用结束即刻释放所分配的内存单元,因此...发生函数调用时, 主调函数实参的值传送给被调函数形参从而实现主...
  • c语言形参和实参传递的区别

    千次阅读 多人点赞 2019-02-28 10:52:49
    参数传递,是在程序运行过程中,实际参数就会将参数值传递给相应的形式参数,然后在函数中实现对数据处理返回的...被调用函数形参只有函数被调用才会临时分配存储单元,一旦调用结束占用的内存便会被释放 值...
  • 问:能否像下边这样用void **指针作为参数,使函数模拟按引用传递参数吗?  void f(void **);  double *dp;  f((void **)&dp); 答:不可移植,这种代码可能有效,而且有时鼓励这样用,但前提是所有指针的内部...
  • 当你选择了一种语言,意味着你还选择...实参:实际参数,是指函数调用时,实际赋值分配内存空间。 例如: int sum(int a, int b) { return a+b; } int main(void) { int c=1; sum(c,2); return 0; } 很明显,函
  • 形式参数:在定义函数时 函数名后面括号中的变量名称为"形式参数...中,实参单元和形参单元是不同的单元。这和java有些不一样 在定义函数中指定的形参,在未出现函数调用时,他们并不占内存中的存储单元。只有在发
  • C语言形参和实参

    万次阅读 多人点赞 2017-09-05 21:44:50
    形式参数(形参):定义函数名函数体时需要用的参数,目的是用来接收调用该函数时传递的参数。 实际参数(实参):传递给被调用函数的值。 通俗的来讲,形参就像物理计算中的公式中的符号,比如Ω,A,V等,而实参...
  • 执行函数调用时实参被传递给形参。  3。传值调用和引用调用指的是用于参数传递过程中的一种机制。传值调用中,只使用了实参的值。传值调用机制里,形参是一个局部变量,其初始值为相应实参的值。...
  • 形参(形式参数) 在函数定义中出现的参数可以看做是一个占位符,它...形参和实参的功能是传递数据,发生函数调用时实参的值会传递给形参形参和实参的区别联系 1 . 形参变量只有在函数调用时才会分配内存...

空空如也

空空如也

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

c语言调用函数时实参和形参

c语言 订阅