从内存角度谈函数调用
C/C++有一个经典的swap函数---交换两个变量的值,最常见的实现方式是通过指针传递变量地址,这样就能在函数内部直接修改原变量的值。比如,对于两个整型(int)变量a和b,可以这样写:voidswap(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}intmain(){inta=10,b=20;swap(&a,&b);//这里要传变量的地址:printf("a=%d\n",a);}swap函数的参数为什么要用指针?要搞清楚这问题,首先要回顾一下指针。▲图1图1说明了指针的用法。这里指针的类型是char*,即指向char的指针。pChar变量只能存储char变量的地址。存储在ch中的值通过指针修改,实现了间接访问(操作)。在vscode里面编辑好代码,并点击右上角的run按钮(假设已经安装了插件),此时操作系统会创建一个进程,其内存布局如图2▲图2一个进程在自已的内存区域中至少包括四个基本部分:代码段、数据段、堆和栈。代码段(TextSegment):存放程序的执行代码,即编译器编译完成的机器码数据段(DataSegment):初始化的全局变量和静态变量(读写)堆(Heap):动态内存分配的区域(如使用malloc申请内存)栈(Stack):用于存放函数的局部变量、调用返回地址等现在来看看main函数是怎么一步步执行的。对于每一个函数调用,进程都会留出部分栈空间,来保存该函数所有的变量和变量值。这些空间称为“栈帧”。在main函数的栈帧中,a和b作为局部变量被保存,如图3所示。▲图3调用函数swap时,将创建新的栈帧,如图4所示。每次函数调用都会创建新的栈帧,该栈帧用来维护函数所有的变量和值。swap函数的栈帧中,a和b同样是局部变量,只不过其值是地址,而且还可以对该地址的变量值进行修改。这样才能实现对a和b的交换。swap函数执行完后,其栈帧会被清空。▲图4如果swap函数不传递变量地址#include<stdio.h>voidswap(inta,intb){inttemp=a;a=b;b=temp;printf("Adressofais%p\n",&a);}intmain(){inta=10,b=20;printf("Adressofais%p\n",&a);swap(a,b);printf("a=%d\n",a);}▲图5可以看到,a的地址是不同的,且无法实现a和b交换。来源:数值分析与有限元编程