天天看点

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

数组元素作为函数参数

  做一道最常见的题目,问 ary[0] 和 ary[1] 的值是否进行交换?

#include <stdio.h>
#include <stdlib.h>

void swap(int x, int y)
{
  int nTmp = 0;
  nTmp = x;
  x = y;
  y = nTmp;
}

int main()
{
  int ary[2] = {7, 8};

  swap(ary[0], ary[1]);
  printf("%08x,%08x\r\n", ary[0], ary[1]);

  system("pause");
  return 0;
}
           

  大家都知道,调用方的传参称为实参,被调方接受的参数称为形参。形参的值改变,不影响实参的值,所以 ary[0] 和 ary[1] 的值没有进行交换。但是好多人却只会背这句话,并不是真正的理解。

  通过函数的设计原则以及机制这篇文章中的知识,分析函数的栈结构,仔细观察内存的分布,来解决这一问题。

  按 F10 进入单步调试,从下到上,可以看到橙色部分是 main 函数的参数,绿色部分是 main 函数的返回地址是,蓝色部分是 main 函数调用方的栈底,紫色部分是 main 函数的局部变量,浅蓝色是寄存器变量。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,地址 0019ff28 写入了 main 函数的 ary[0] 的值 7 ,地址 0019ff2c 写入了 main 函数的 ary[1] 的值 8。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  main 函数栈的范围从地址 0019fedc 到地址 0019ff44。esp 的值就是专门指向栈顶的,它的值是地址 0019fedc 。ebp 的值就是专门指向栈底的,他的值是地址 0019ff30。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  地址 0019fedc 上面是空闲区。根据函数传参原则,反向传参,先传 ary[1],再传 ary[0],可以看到,地址0019fed8 写入了 swap 函数的 y 的值 8 ,地址 0019fed4 写入了 swap 函数的 x 的值 7,地址004125d3 是 swap 函数的返回地址。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,main 函数的栈底被写入,然后一大片的 cccccccc 作为局部变量,紫色框部分作为寄存器变量。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,地址 0019fec8 处写入了 nTmp 的值 0。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,x 的值等于 ary[0] 等于 7,所以 nTmp = x,地址 0019fec8 写入了 7。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

 再按 F10 ,y 的值等于 ary[1] 等于 8,所以 x = y,地址 0019fed4 写入了 8。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,nTmp 的值等于 7,所以 y = nTmp,地址 0019fed8 写入了 7。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  函数执行完毕,可以看到 main 函数中地址 0019ff28 和地址 0019ff2c 中的值 ary[0] 和 ary[1],并没有被修改。

  计算机中的传参是拷贝,不是剪切。传参之后原内存内容不变,复制了一份副本,放在了其他栈空间。栈空间的一切操作,只针对这个函数所占有的栈空间的内存的一切操作,并不影响其他函数栈空间的内存中的值的改变。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

数组地址作为函数参数

  将上面的程序稍作修改,问 ary[0] 和 ary[1] 的值是否进行交换?

#include <stdio.h>
#include <stdlib.h>

void swap(int a[])
{
  int nTmp = 0;
  nTmp = a[0];
  a[0] = a[1];
  a[1] = nTmp;
}

int main()
{
  int ary[2] = {7, 8};

  swap(ary);
  printf("%08x,%08x\r\n", ary[0], ary[1]);

  system("pause");
  return 0;
}
           

  同样通过调试,深入理解这个程序。

  按 F10 进入单步调试,数组名 ary 代表,第 0 个元素的地址常量。观察框中显示 ary 的值是 0019ff28。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,地址 0019ff28 写入了 ary[0] 的值 7 ,地址 0019ff2c 写入了 ary[1] 的值 8。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  按 F11 进入 swap ,数组 ary 第 0 个元素的地址常量 0019ff28 ,作为实参传入了 swap 函数的形参内存中。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  再按 F10 ,swap 函数的局部变量区,地址 0019fecc 写入了 nTmp 的值 0。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

  在下一步,单步调试之前,需要说明几个知识点。

  是否影响实参,主要看函数内有没有对实参的地址进行“间接访问”,有间接访问,就会影响到函数外,没间接访问,怎么做都不影响。

  []下标运算可实现间接访问,根据数组这篇文章的知识点,通过下标运算求出地址后,按照数组元素类型去解释这个地址,就是下标访问。

  a 的值是 0019ff28。

  a[0] 做下标运算 &a[0] = (int)a + sizeof(int)*0 = 0019ff28,对 地址 0019ff28 解释为数组元素的类型(整形),就是 7。

  a[1] 做下标运算 &a[1] = (int)a + sizeof(int)*1 = 0019ff2c,对 地址 0019ff2c 解释为数组元素的类型(整形),就是 8。

  再按 F10 ,进行单步调试,通过间接访问,地址 0019ff28 的值 7。复制给 nTmp。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

 再按 F10,通过间接访问,地址 0019ff2c 的值 8 。再通过间接访问,将 8 写入到 0019ff28 的地址中。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

 再按 F10,nTmp 的值 7 ,通过间接访问,写入到地址 0019ff2c 中。

 成功实现了 ary[0] 和 ary[1] 的互换。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

 好多人会以为,传入的是地址,就会影响到实参,其实只说对了一半。真正影响到实参,是进行了间接访问。

 为了更直观的在程序中体现,传入的实参是常量。将第 0 个元素的地址常量 0x0019ff28 直接写入程序。程序修改为, swap(ary) 换为 swap(0x0019ff28)。

#include <stdio.h>
#include <stdlib.h>

void swap(int a[])
{
  int nTmp = 0;
  nTmp = a[0];
  a[0] = a[1];
  a[1] = nTmp;
}

int main()
{
  int ary[2] = {7, 8};

  swap(0x0019ff28);
  printf("%08x,%08x\r\n", ary[0], ary[1]);

  system("pause");
  return 0;
}
           

 同样可以,交换 ary[0] 和 ary[1] 的值。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

 传入数组 ary 第 0 个元素的地址常量,但不进行间接访问。形参是变量,可以进行 ++,所以 a++。

#include <stdio.h>
#include <stdlib.h>

void swap(int a[])
{
  int nTmp = 0;
  a++;
}

int main()
{
  int ary[2] = {7, 8};

  swap(ary);
  printf("%08x,%08x\r\n", ary[0], ary[1]);

  system("pause");
  return 0;
}
           

 就好比,现实中打电话。知道电话号码,叫出这个人(间接访问),在这个人脸上画图案,写字都可以(间接访问后修改值)。但是,知道电话号码,在电话号码上进行加一,不呼叫这个人,那么对这个人不能造成任何影响。

 并没有改变 ary[0] 和 ary[1] 的值,输出结果仍为 7, 8。

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

数组作为函数参数更多精彩文章,关注公众号。想了解更多 C/C++ 或 网络安全知识,请关注公众号:大人物小城梦

继续阅读