明明白白 c/c++ 函数 参数问题, 函数内部分配空间

c/c++ 中函数参数问题:

1 函数参数传递的方式
c中参数传递有两种方式:值传递,指针传递。
c++中多了一种:引用传递。
1.1值传递
void swap(int a, int b)
{
   inttmp = a;
   a =b;
   b =tmp;
}

调用上面函数的时候:
int main()
{
int a = 5;
int b = 3;
swap(a,b);
printf("a=%d,b=%d",a,b);
}
输出的结果是5,3.
原因在下面讲述:
先说一个概念,形参和实参。
在程序执行的时候,执行到红色的 swap(a,b) ,这里的a,b就是实参,这里a,b都是有明确值的,a的值是5,b的值是3.
这个时候,程序就会继续的往下执行,大家都知道,程序会调用蓝色的 voidswap(a,b ),当调用到这个函数的时候,程序的实际运行的过程是这样的:
首先声明两个局部的变量a,b,这两个变量就是形参。 这两个变量的作用域只在蓝色的区域内,等出了蓝色的区域,他们就不存在了。上面这句话很重要,也是下面所有的基础。
第二步,会将外部的a,b的值付给函数内部的局部变量的a,b。这样讲很多初学者容易搞糊涂,直白的说,函数内部的a,b和函数外面的a,b的内存空间不一样,内容一样而已。如果将程序改成 
int num1 = 5 ,numb2 = 3;
swap(num1, num2);
可能就好理解了,num1,num2只是把自己的值传递给了里面的a,b.但是a,b无法去改变外面的num1和num2.
第三步,函数内部的a,b进行交换,但是函数外面的a,b,由第二步可以知道并不会收到影响。
第四步,删除内部的a,b。

1.2 指针传递
指针传递的含义很简单,就是讲变量的指针传递到函数内部,程序可以改变变量指针指向的内容, 但是不能改变指针本身。这句话很重要,也是下面我们将的关键。
还以上面的例子来讲,为了方便理解我换了外面的变量名字。
void swap(int*a, int *b)
{
   int tmp =*a;
   *a =*b;
   *b =tmp;
}
调用上面函数的时候:
int main()
{
int num1 = 5;
int num2 = 3;
swap(&num1,&num2);
printf("num1 =%d,num2=%d",num1,num2);
}
这一次输出的结果就是3,5了。
我们分析一下这个过程。 执行到红色的 swap(&num1,&num2)
第一步,蓝色的 swap(int *a, int*b) 会先申请两个局部变量 *a,*b。
第二步,分别指向num1,和num2的地址。
第三步,将num1的地址里面的数据和num2地址里面的数据进行交换。
第四步,删除内部的*a,*b。

我们看下面一个例子,
void swap(int *a, int *b)
{
   int *tmp;
   tmp = a;
    a= b;
    b= tmp;
}

int main()
{
int num1 = 5;
int num2 = 3;
swap(&num1,&num2);
printf("num1 =%d,num2=%d",num1 ,num2);
}
大家可以试试输出的结果是什么样的?

答案可能会出乎一部分人的意料,num1 =5 ,num2=3;
很多人就会说了,不是值传递不会改变值,但是指针传递会改变值。可能大多时候学生为了应付考试的需要,就简单的把这些东西死记硬背下来,没有完全搞懂形参和实参的真正含义。我们还按照上面的分析。
第一步,蓝色的 swap(int *a, int*b)  会先申请两个局部变量 *a, *b。
第二步,分别指向num1,和num2的地址。
第三步,这一步和上面的不一样,内部的*a,和*b指向的地址发生了改变,外面的num1和num2不受影响。
第四步,删除内部的*a,*b。

如果我们想改变指针本身的话,我们怎么做呢?
其实很简单,传递一个二维指针。同样上面的例子:
void swap(int**a, int **b)
{
    int*tmp;
    tmp =*a;
    *a =*b;
    *b =tmp;
}

#include <cstdio>
void swap(int **a, int **b)
{
    int *tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

int main()
{
int num1 = 5;
int num2 = 3;
int *p = &num1;
int *q = &num2;
swap(&p, &q);
printf("num1=%d,num2=%d\n",num1 ,num2);
printf("*p=%d,*q=%d",*p ,*q);
}
结果为
num1 = 5, num2 =3
*p =3, *q =5
上面已经分析那么多了,这里简单分析一下,程序只是会改变*p,和*q指向的地址,而num1和num2本身就是初始化的时候将地址赋给了num1和num2,,所以无法改变他们的地址。
 ps:num1和num2就像是房子,分配了房子,最多是能改变房子里面的东西,但是却不能修改房子本身。而*p和*q只是记录员记录的房子号码,所以可以随便修改指向。计算机还是很讲道理的,不像很多拆迁的,连房子带里面的东西一窝端了。

1.3 引用传递
引用其实就是别名的意思,和上面优点不一样,是c++的特性。看下面的例子
void swap(int&a, int &b)
{
   int tmp =a;
   a = b;
   b =tmp;
}

调用上面函数的时候:
int main()
{
int a = 5;
int b = 3;
swap(a,b);
printf("a=%d,b=%d",a,b);
}

输出的结果是a = 3, b =5.
这个就不能够按照上面的去理解了,我是这么猜测的,前面的两个属于c的特性,所以实质上面是一样的,仔细的品味,你会发现前面的值传递和指针传递的意义一样,都不能改变传入的对象(值的内容,或者指针本身。但是引用传递的话就相当于将a,b的作用域扩充到函数swap内部了。
  

2 如何在函数中分配空间。
这个是本人经常犯的一个错误,而且经常搞混淆,这个也是我们要重点讲的内容,如果你对前面的理解的很清楚,这里也很好理解了。
例如:目的通过函数调用申请一块动态内存。
有人会这么写:
void allocmemory(int *p)
{
   p  = newint[10];
   p[9] = 1;
}

int main()
{
   int *p;
   int a;
    allocmemory(p);
   a = p [9];
   cout<<a;//这里用c++的输出了,有一些关于c/c++输出的优劣,这里就不讨论了。
   delete []p;
}

这个程序肯定会崩溃掉,而且没有任何打印。(这里没有打印的原因是,打印是放在缓存buf里面,优先级比较低,程序崩溃的优先级要高一点,后面出问题了,buf还没有满,所以不会有打印,可以增加endl便可以打印出来,所以有时候查问题光看打印是不够的。)
但是单步调试,或者删除最后一行delete []p,你会发现输出的结果是0,或者是其他数字。
原因前面讲过了,函数内部分配了一个数组,并将数组的指针指向了形参的p,但是函数执行完毕,形参p释放了,外面的实参p并没有发生改变。
我们怎么做可以得到正确的结果?
第一个是二维指针。
void allocmemory(int **p)
{
  *p  = new int[10];
  (*p)[9] = 1;
}

int main()
{
  int *p;
  int a;
   allocmemory(&p);
  a = p [9];
  cout <<a;
  delete []p;
}
第二个是利用返回值。(返回值比较特殊,它能将返回的那个变量保存到函数运行结束)
int* allocmemory()
{
  int *p  = new int[10];
  p[9] = 1;
   return p;
}

int main()
{
  int *p;
  int a;
  p =  allocmemory();
  a = p [9];
  cout <<a;
  delete []p;
}
这个也是malloc函数工作的原理。(void*)malloc(int length);
第三 更高级的方式 指针的引用。
#include <iostream>
#include <cstdio>
using namespace std;
void allocmemory(int *&p)
{
   p  = newint[10];
   p[9] = 1;
}

int main()
{
   int *p;
   int a;
   allocmemory(p);
   a = p [9];
   cout<<a;
   delete []p;
}

  

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值