函数调用时的形参与实参

    大多数人在进行学习编程时,对于函数调用时函数时的传递的内容是一个头大的问题。本人在学习后有了以下的见解请大家共同学习,若有失误,请指出!

基础知识

    1.系统堆栈(即栈区域)和堆区域。
        1.1、栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有FIFO的特性,在编译的时候可以指定需要的Stack的大小。在编程中,例如C/C++中,所有的局部变量都是从栈中分配内存空间,实际上也不是什么分配,只是从栈顶向上用就行,在退出函数的时候,只是修改栈指针就可以把栈中的内容销毁,所以速度最快。但是栈区域有一个很大的确定就是由于它通常只有1到2M大小的空间,这个大小相对于内存中的4G或者8G空间是很小的。所以编写递归程序时候,使用Stack是需要考虑到递归的深度。
      1.2、堆(Heap)是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程,C/C++分别用malloc/New请求分配Heap,用free/delete销毁内存。由于从操作系统管理的内存分配所以在分配和销毁时都要占用时间,所以用堆的效率低的多!但是堆的好处是可以做的很大,C/C++对分配的Heap是不初始化的。在没有垃圾回收机制的编程语言中如果程序员在编写一些长时间需要运行的程序时员(例如:服务器程序),对于堆的使用需要万分小心。如果某程序员在编写程序时候申请了堆区域的空间但 没有去释放(即free/delete)且该程序需要长时间运行,这是很可怕的。这存在一个很大的问题 内存泄漏!!! 这是因为内存都被这个程序了。该程序在malloc/New用过后,扔在一边没有归还操作系统还不停的申请内存空间。长此以往,系统会被其消耗殆尽。

形参与实参的传递

        1、

            先举一个错误的新手容易犯的错误(该程序意用程序实现在交换两个空间的值)

#include <stdio.h> 

void exchange(int one,int another);

void exchange(int one,int another) {
	int tmp;

	tmp = one;
	one = another;
	num2 = tmp;
}

int main() {
  	int num1;
  	int num2;

  	scanf("%d%d",&num1,&num2);

  	printf("交换前的值为:%d %d\n",num1,num2);

  	exchange(num1,mun2);

  	printf("交换后的值为:%d %d\n",num1,num2);

        return 0;
}

结果如下:

    对于大多数的编程新手来说,第一次编写类似的代码很容易写成这种情况吧。下面我解释一下,这种方法中,看似交换了num1和num2的值,但实际上。这两个值并没有交换。在主函数中。在堆栈区域申请了两个整形变量空间即num1,和num2的空间。在调用exchange函数时候会现有现场信息(简称:主现。在不涉及多线程中,这里就是eip的值。)保存在堆栈区域,栈顶指针移动向上移动一定的距离。这是为了在此运行主函数时候恢复上次的状态。然后将num2复制一份入栈,another的值对应这个空间的值,栈顶指针向上移动一个整形空间,再将num1复制一份入栈,one的值对应这个空间的值。栈顶指针在向上移动一个整形空间。然后进入到exchange中在堆栈中申请一个整形空间,栈顶指针向上移动一个整形空间。然后进行交换one和another的交换。然后在将栈顶指针恢复到主现以前。然后运行主函数中的代码。上述文字如下图。

接下来我说一种正确代码

#include <stdio.h> 

void exchange(int* one,int* another);

void exchange(int* one,int* another) {
	int tmp;

	tmp = *one;
	*one = *another;
	*another = tmp;
}

int main() {
  	int num1;
  	int num2;

  	scanf("%d%d",&num1,&num2);

  	printf("交换前的值为 :%d %d\n",num1,num2);

  	exchange(&num1,&num2);

  	printf("交换后的值为 :%d %d\n",num1,num2);

        return 0;
}

执行结果为:



上面的代码成功的交换了num1和num2的数值。在主函数中,在堆栈区域申请了两个整形变量空间即num1,和num2的空间。在调用exchange函数时候会现有现场信息(简称:主现。在不涉及多线程中,这里就是eip的值)保存在堆栈区域,栈顶指针移动向上移动一定的距离。这是为了在此运行主函数时候恢复上次的状态。然后将num2地址的值复制一份入栈,another的值对应这个空间的值,栈顶指针向上移动一个整形空间,再将num1地址的值复制一份入栈,one的值对应这个空间的值。栈顶指针在向上移动一个整形空间。然后进入到exchange中在堆栈中申请一个整形空间,栈顶指针向上移动一个整形空间。然后将one指向空间的值赋值给tmp,然后将another所指向空间的值赋值给one所指向空间,最后将tmp的值赋值给another所指向的空间。这就完成了num1和num2的交换。然后在将栈顶指针恢复到主现以前。然后运行主函数中的代码。上述文字如下图



总结提高

        在上面的两个例子中,在第一个从错误例子中,我们能发现一个问题,不论我们怎么操作传递到子函数的形参的值,都无法修改主调函数中变量的值。但在第二个例子中,我们可以通过传递主调函数变量的地址值并进行“*”操作就可以改变主调函数中变量的值。这给了我们一个启示:在调用函数中如果不需要修改主调函数中变量的值,传递那个变量的值。如果需要改变主调函数中变量的值,这时候就需要传递的就是该变量的地址的值。但由于传递的值是需要入栈,当传递的变量非常大的时候就会存在危险即栈溢出。所以当需要传递的内容比较大的时候,这时需要传递的仍然是变量的地址,但为了防止被调函数对变量操作。这时候需要加上const!!!

#include <stdio.h> 

void exchange(const int* one,const int* another);

void exchange(const int* one,constint* another) {
	int tmp;

	tmp = *one;
	*one = *another;
	*another = tmp;
}

int main() {
  	int num1;
  	int num2;

  	scanf("%d%d",&num1,&num2);

	exchange(&num1,&num2);
  	
    return 0;
}

编译会提示错误如下:


这就对主调函数中变量的值无法操作。






猜你喜欢

转载自blog.csdn.net/lixiaotianx/article/details/79949862