C语言函数调用问题汇总

1. 形参问题

形参——形式化的参数,这种解释很抽象。

在任何编程语言中,任何变量都是某个特定的存储空间的一段而已,这一段具体有多大,由变量的类型决定,如 int 型,则为4 Byte。

在进行函数/方法的调用时,函数的实参是存在于存储空间某个具体位置的一段指定空间的,而任何函数或方法都会被程序控制器开辟一个栈空间(stack),这个空间也是一段存储位置,有时候为了效率起见,它存在于计算机的ram或是CPU高速寄存器中。

调用的过程,实际上就是从实参的存储空间复制值到形参栈空间的过程。如下:

C语言函数调用问题汇总_第1张图片

  • 实际上,几乎在所有的编程语言中,形参在函数未被调用之前都是没有分配存储空间的。

形参存的存储空间,存在于函数的栈空间中,因此,在函数未被调用之前,栈空间实际上是不存在的,也就是说,形参在未被调用之前,没有被分配存储空间。

  •  形参的生命周期

C语言函数调用问题汇总_第2张图片

  • 代码实例

#include "stdio.h"

void f(int x){
    x=99;
    return;
}
int main(void){
    int x=10;
    printf("before callback, x=%d\n",x);
    f(x);
    printf("after callback, x=%d\n",x);
    return 0;
}

  • 结果

可见,实际上在调用中对实际参数并未形成影响。调用中的处理都是对实参的“被复制的那一份”进行的操作。



2. 在C语言中,当有多个函数的返回值时,不能用普通的 return 的方式实现。需要通过传回地址的形式进行,即地址/指针传递。

  • 数组作为参数的函数调用方式是地址传递!
  • 一维数组的数组名表示数组中0下标元素的地址。不管数组元素中第0个元素为什么类型,数组名都表示一个地址,不能看成整型。
  • 地址传递中,形参和实参将指向相同的地址空间。虽然在这种传递方式中,函数调用过程仍然在一个栈空间中进行,但因为其操作中会对地址空间所指向的变量进行操作,因此实际上改变了传入参数的值。

C语言函数调用问题汇总_第3张图片

在上述的形参实参传递过程中,形参和实参都指向相同的内存空间(即实参的数组本身),只不过传递过程中,只是告知了函数该实参的首个元素的地址,因此在用数组名作为参数时还需指定数组长度

  • 数组为形参时,实际上数组变量是一个指针变量。在函数调用完成后,指针变量被销毁,但指针变量所指向的内存空间(也就是实参数组的空间)依然存在,不会也无法被销毁。


在调用中,需要注意几点:
    • 形参实参必须为相同的数组类型
    • 在主调和被调函数中需分别定义数组
    • 在编译过程中对形参的数组大小不做检查,因此可以不指定大小,形如“void fun(int [])”是合法的
    • 如果在形参中指定了数组的大小,则必须在传入实参时注意其大小,因为我们通常假定,对形参的处理中,是按照这个形参中给出的大小来计算的。
    • [请注意:]当然,如果在形参中按"void fun(int a[10])"的方式定义被调函数,却在函数内使用了a[10]、a[11],那么,如果实参中的对应a[10]、a[11]合法时,也不会出错!
  • 代码实例
    #include "stdio.h"
    
    void change(int a[],int n){
        int i,j,temp;
        for(i=0,j=n-i-1;i

  • 结果
显然,传入的参数为 b[10],结果在对形参 a[10] 的处理中,将实参数组 b[10] 进行了操作!


3. 如果在函数调用中参数列表不是数组形式或指针,那么几遍主调函数传入了数组的某个元素,仍然是值传递而非地址传递。如:
#include "stdio.h"

void change2(int x){
    x+=1000;;
    return;
}
void p(int a[],int n){
    for(int i=0;i

结果如下:

可见,第三个元素也即是 b[2] 本身没有在函数调用过程中被更改。


4.多维数组在函数参数列表中,必须指明第一维之后的所有维的大小!第一维是可以忽略的。如下定义方式都是合法且等价的。

void f(int a[5][10]);

void f(int a[][10]);

而如下方式则是非法的:

void f(int a[5][]);

void f(int a[][]);

你可能感兴趣的:(C语言)